import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as AWS from 'aws-sdk';
import * as moment from 'moment';
import { SharpService } from 'nestjs-sharp';
import * as sharp from 'sharp';
import * as simpleThumbnail from 'simple-thumbnail';
import * as mime from 'mime-types';
import * as fs from 'fs';
import * as path from 'path';

@Injectable()
export class GlacierService {
    private readonly s3: AWS.S3;
    private readonly bucketName;
    private readonly do_base_url;
    private readonly URL_VIDEO;
    private readonly region;
    constructor(
        private readonly configService: ConfigService,
        private readonly sharpService: SharpService
    ) {
        const spaceEndpoint = this.configService.get<string>('DO_ENDPOINT');
        this.do_base_url = this.configService.get<string>('BASE_URL');
        this.region = this.configService.get<string>('DO_REGION');
        this.URL_VIDEO = this.configService.get<string>('URL_VIDEO');
        this.s3 = new AWS.S3({
            accessKeyId: this.configService.get<string>('DO_ACCESS_KEY'),
            secretAccessKey: this.configService.get<string>('DO_SECRET_ACCESS_KEY'),
            endpoint: spaceEndpoint,
            region: this.region, // Adjust this based on your region
            s3ForcePathStyle: true
        });
        this.bucketName = this.configService.get<string>('BUCKET_NAME');
    }

    async convertSvgToPng(buffer: Buffer, outputPath: string): Promise<string> {
        try {
            // Read the SVG file
            // Convert to PNG using Sharp
            await sharp(buffer).png().toFile(outputPath);
            return `SVG successfully converted to PNG: ${outputPath}`;
        } catch (error) {
            throw new Error(`Error during SVG to PNG conversion: ${error.message}`);
        }
    }

    uploadFile = async (files: any) => {
        try {
            let { originalname, buffer, mimetype } = files;
            let file_name = await this.generate_file_name(originalname);
            let mime_type = mimetype.split("/");
            console.log("mime_type" , mime_type)
            let response: { file_name?: any; tumbnails?: any; base_url?: string; type?: string; folders?: string[]; message?: string; };
            if (mime_type[1]?.includes('svg')) {
                let name = originalname.split('.');
                const outputDir = path.join(__dirname, `../../images/${name}.png`);
                await this.convertSvgToPng(buffer, outputDir);
                let fetch_files = path.join(__dirname, `../../images/${name}.png`);
                response = await this.upload_images_original_svg(name + ".png", fetch_files, 'image');
                let fetch_file = path.join(__dirname, `../../images/${name}.png`);
                try {
                    fs.unlinkSync(fetch_file);
                } catch (unlinkErr) {
                    console.error(`Error deleting file: ${fetch_file}`, unlinkErr);
                }
            }
            else if (mime_type[0] == 'image' && !mime_type[1]?.includes('svg')) {
                response = await this.upload_images(file_name, buffer, mimetype);
            } else if (mime_type[0] == "audio") {
                response = await this.upload_audio(file_name, buffer, mimetype);
            } else if (mime_type[0] == "video") {
                response = await this.upload_video(file_name, buffer, mimetype);
            } else if (mime_type[0] == "application" ||
                mime_type[0] == "documents") {
                response = await this.upload_doc(file_name, buffer, mimetype);
            } else {
                response = {
                    message: "Sorry we currently do not support this format",
                };
            }
            let new_response = response;
            return { data: new_response };
        } catch (err) {
            throw err
        }
    }


    upload_thumbnails = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            await this.uploadMultipartFile(file_name, 'images/thumbnail', buffer, mime_type)
            let response = {
                base_url: this.do_base_url,
                type: 'VIDEO',
                folders: ['video'],
                file_name: file_name
            }
            return response
        }
        catch (err) {
            throw err;
        }
    }

    generate_file_name_for_thumnails = async (file_name: string) => {
        try {
            let current_millis = moment().format('x')
            let raw_file_name = file_name.split(/\s/).join('');
            let split_file = raw_file_name.split('.')
            let split_all = split_file[0].split(/[^a-zA-Z0-9]/g).join('_')
            let name = split_all.toLowerCase();
            // let ext = split_file[1]
            let ext = file_name.split('.').pop();
            let gen_file_name = `${name}_${current_millis}.${ext}`
            return gen_file_name.toLowerCase()
        }
        catch (err) {
            throw err;
        }
    }

    create_thumbnails = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            let file_url = `${this.URL_VIDEO}/${file_name}`;
            // let output_path = path.resolve(__dirname, `../../src/public/images/${originalname}_thumbnail.png`);
            let name = file_name.split('.mp4').join('.png')
            let output_path = path.resolve(__dirname, `../../images/${name}`)
            let gen_thumb = await this.gen_thumbnail(file_url, output_path);
            if (gen_thumb == true) {
                let fetch_file = path.resolve(__dirname, `../../images/${name}`);
                let mime_type = mime.lookup(fetch_file);
                let file_buffer = fs.readFileSync(fetch_file);
                let file_name = await this.generate_file_name_for_thumnails(name)
                await this.upload_thumbnails(file_name, file_buffer, mime_type);
                let s3_file_name = file_name
                if (s3_file_name != undefined) {
                    // if (fs.existsSync(fetch_file)) {
                    fs.unlinkSync(fetch_file)
                    // }
                }
                
                return s3_file_name;
            } else {
                return null;
            }
        } catch (err) {
            console.log("")
            throw err;
        }
    }

    gen_thumbnail = async (imagePath: string, outputPath: string): Promise<boolean> => {
        const size = '250x?'
        try {
            const outputDir = path.dirname(outputPath);
            if (!fs.existsSync(outputDir)) {
                fs.mkdirSync(outputDir, { recursive: true });
            }
            await simpleThumbnail(imagePath, outputPath, size);
            return true;
        } catch (error) {
            console.error('Error generating thumbnail:', error);
            return false;
        }
    };

    upload_audio = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            // await this.upload_file_to_spaces(file_name, 'audio', mime_type, buffer)
            await this.uploadMultipartFile(file_name, 'audio', buffer, mime_type)
            let response = {
                base_url: this.do_base_url,
                type: 'AUDIO',
                folders: ['audio'],
                file_name: file_name,
                file_url: `${this.do_base_url}/uploads/audio/${file_name}`,
            }
            return response
        }
        catch (err) {
            throw err;
        }
    }

    upload_video = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            await this.uploadMultipartFile(file_name, 'video', buffer, mime_type)
            let fetch_thumbnails = await this.create_thumbnails(file_name, buffer, mime_type);
            let response = {
                base_url: this.do_base_url,
                type: 'VIDEO',
                folders: ['video'],
                file_name: file_name,
                tumbnails: `${this.do_base_url}/uploads/images/thumbnail/${fetch_thumbnails}`,
                video_url: `${this.do_base_url}/uploads/video/${file_name}`
            }
            return response
        }
        catch (err) {
            throw err;
        }
    }


    upload_doc = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            await this.uploadMultipartFile(file_name, 'documents', buffer, mime_type)
            // await this.upload_file_to_spaces(file_name, 'documents', mime_type, buffer)
            let response = {
                base_url: this.do_base_url,
                type: 'DOCUMENT',
                folders: ['documents'],
                file_name: file_name
            }
            return response
        }
        catch (err) {
            throw err;
        }
    }

    async upload_images(name: string, buffer: any, mime_type: string) {
        try {
            let file_name = await this.generate_file_name(name)

            let original_buffer = await this.create_sharp_file(buffer, null);
            await this.uploadMultipartFile(file_name, "images/original", original_buffer, mime_type)

            let medium_buffer = await this.create_sharp_file(buffer, 500);
            await this.uploadMultipartFile(file_name, "images/medium", medium_buffer, mime_type)

            let small_buffer = await this.create_sharp_file(buffer, 100);
            await this.uploadMultipartFile(file_name, "images/small", small_buffer, mime_type)

            let response = {
                base_url: this.do_base_url,
                type: 'IMAGE',
                // folders: ['original',],
                folders: ['original', 'medium', 'small'],
                file_name: file_name,
                file_url: `${this.do_base_url}/uploads/images/original/${file_name}`,
                medium_file_url: `${this.do_base_url}/uploads/images/medium/${file_name}`,
                small_file_url: `${this.do_base_url}/uploads/images/small/${file_name}`,
            }

            return response
        }
        catch (err) {
            throw err;
        }
    }


    async upload_images_original_svg(name: string, buffer: any, mime_type: string) {
        try {
            let file_name = await this.generate_file_name(name)
            let original_buffer = await this.create_sharp_file(buffer, null);
            await this.uploadMultipartFile(file_name, "images/original", original_buffer, mime_type)
            let response = {
                base_url: this.do_base_url,
                type: 'IMAGE',
                folders: ['original',],
                // folders: ['original', 'medium', 'small'],
                file_name: file_name,
                file_url: `${this.do_base_url}/uploads/images/original/${file_name}`,
            }

            return response
        }
        catch (err) {
            throw err;
        }
    }

    generate_file_name = async (file_name: string) => {
        try {
            let current_millis = moment().format('x')
            let raw_file_name = file_name.split(/\s/).join('');
            let split_file = raw_file_name.split('.')
            let split_all = split_file[0].split(/[^a-zA-Z0-9]/g).join('_')
            let name = split_all.toLowerCase()
            let ext = file_name.split('.').pop();
            let gen_file_name = `${name}_${current_millis}.${ext}`
            return gen_file_name.toLowerCase()
        }
        catch (err) {
            throw err;
        }
    }

    create_sharp_file = async (buffer: any, pixels: any) => {
        try {
            if (pixels) {
                return await this.sharpService.edit(buffer).resize(pixels).rotate().toBuffer();
            } else {
                return await this.sharpService.edit(buffer).rotate().toBuffer();
            }
        }
        catch (err) {
            throw err;
        }
    }

    async uploadMultipartFile(name: any, folder_name: string, buffer: any, mimetype: any) {
        const partSize = 5 * 1024 * 1024; // 5MB chunks
        const params: AWS.S3.CreateMultipartUploadRequest = {
            Bucket: this.bucketName,
            Key: `uploads/${folder_name}/${name}`, // Ensure consistent key name
            ContentType: mimetype,
            ACL: 'public-read'
        };
        try {
            const multipartUpload = await this.s3.createMultipartUpload(params).promise();
            const uploadId = multipartUpload.UploadId;
            const numParts = Math.ceil(buffer.length / partSize);
            const uploadPromises: Promise<AWS.S3.CompletedPart>[] = []; // Adjusted to expect CompletedPart
            for (let i = 0; i < numParts; i++) {
                const start = i * partSize;
                const end = Math.min(start + partSize, buffer.length);
                const partParams: AWS.S3.UploadPartRequest = {
                    Bucket: this.bucketName,
                    Key: `uploads/${folder_name}/${name}`, // Ensure consistent key name
                    PartNumber: i + 1,
                    UploadId: uploadId as string,
                    Body: buffer.slice(start, end),
                };
                const uploadPromise = this.s3.uploadPart(partParams).promise();
                uploadPromises.push(uploadPromise);
            }
            const uploadedParts = await Promise.all(uploadPromises);
            const completedParts: AWS.S3.CompletedPart[] = uploadedParts.map((part, index) => ({
                ETag: part.ETag,
                PartNumber: index + 1,
            }));
            const completeParams: AWS.S3.CompleteMultipartUploadRequest = {
                Bucket: this.bucketName,
                Key: `uploads/${folder_name}/${name}`, // Ensure consistent key name
                // ACL: 'public-read',
                UploadId: uploadId as string,
                MultipartUpload: {
                    Parts: completedParts,
                },
            };
            let upload_data = await this.s3.completeMultipartUpload(completeParams).promise();
            return upload_data
        } catch (err) {
            console.error('Error uploading file:', err);
            throw err;
        }
    }
}









