
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
// import { SharpService } from 'nestjs-sharp';
import  moment from 'moment';
import * as AWS from 'aws-sdk';
// import * as generate_thumbnail from 'simple-thumbnail';
// import * as mime from 'mime-types';
// import * as path from 'path';
import { Mixin } from "ts-mixer";
import * as DTO from "./dtos/index";
// import { DatabaseService } from 'src/database/database.service';
// import { CommonService } from "../common/common.service";
// import { AuthService } from './../auth/auth.servcie';
// import * as ffmpeg from 'fluent-ffmpeg';
// import * as ffmpeg from "ffmpeg";
// const genThumbnail = require('simple-thumbnail')
import * as gen_thumbnail from "simple-thumbnail";
import * as path from "path";
import * as fs from "fs";
import * as mime from 'mime-types';
import  sharp from "sharp";
// const sharp = require('sharp');
const { promisify } = require('util');
const convert = require('heic-convert');



class Helper {

    protected s3: AWS.S3;
    protected bucket: string;
    protected base_url: string;
    protected url: string;
    protected endpoint: string;
    protected cred_endpoint: string;

    constructor(
        // protected Models: DatabaseService,
        // protected CommonService: CommonService,
        protected ConfigService: ConfigService,
        // protected AuthService: AuthService,
        // protected SharpService: SharpService
    ) {
        this.base_url = this.ConfigService.get<string>('URL');
        this.bucket =  this.ConfigService.get<string>('BUCKET_NAME');
        this.endpoint = this.ConfigService.get<string>('AWS_ENDPOINT');
        this.cred_endpoint = this.ConfigService.get<string>('ENDPOINT');
        
        const AWS_ACCESS_KEY = this.ConfigService.get<string>('AWS_ACCESS_KEY');
        const AWS_SECRET_KEY = this.ConfigService.get<string>('AWS_SECRET_KEY');
        const AWS_REGION = this.ConfigService.get<string>('AWS_REGION');

        
        this.s3 = new AWS.S3({
            region: AWS_REGION,
            endpoint:this.cred_endpoint,
            credentials: {
                accessKeyId: AWS_ACCESS_KEY,
                secretAccessKey: AWS_SECRET_KEY,
            }
        });
    }

    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('.')

            // ! spiting by all special charcters
            let split_all = split_file[0].split(/[^a-zA-Z0-9]/g).join('_')

            let name = split_all.toLowerCase()
            let ext = split_file[1]
            let gen_file_name = `${name}_${current_millis}.${ext}`
            return gen_file_name.toLowerCase()
        }
        catch (err) {
            console.log("generate_file_name....err", 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();
            // }
            if (pixels) {
                return await sharp(buffer).resize(pixels).rotate().toBuffer()

                // return await sharp.edit(buffer).resize(pixels).rotate().toBuffer();
            }
            else {
                return await sharp(buffer).rotate().toBuffer()
                // sharp.edit(buffer).rotate().toBuffer();
            }
        }
        catch (err) {
            console.log("create_sharp_file....err", err);
            throw err;
        }
    }

    upload_images = async (file_name: string, buffer: any, mime_type: string) => {
        try {
            console.log("mime_type....", mime_type);
            if (mime_type == 'image/gif') {
                await this.upload_file_to_s3(file_name, 'gif', mime_type, buffer)
                let response = {
                    message: "File uploaded successfully.",
                    base_url: `${this.endpoint}/uploads/gif/${file_name}`,
                    file_name: file_name
                }
                return response
            }
            else if (mime_type == 'image/heic') {
                // const inputBuffer = await promisify(fs.readFile)('/path/to/my/image.heic');
                const output_buffer = await convert({
                    buffer: buffer,   // the HEIC file buffer
                    format: 'PNG'    // output format
                });
                console.log("output_buffer....", output_buffer)

                let original_buffer = await this.create_sharp_file(output_buffer, null);
                await this.upload_file_to_s3(file_name, 'images/original', mime_type, original_buffer)

                let medium_buffer = await this.create_sharp_file(output_buffer, 250);
                await this.upload_file_to_s3(file_name, 'images/medium', mime_type, medium_buffer)

                let small_buffer = await this.create_sharp_file(output_buffer, 100);
                await this.upload_file_to_s3(file_name, 'images/small', mime_type, small_buffer)
                let response = {
                    message: "File uploaded successfully.",
                    base_url: `${this.endpoint}/uploads/images/original/${file_name}`,
                    file_name: file_name
                }
                return response
                // await this.upload_file_to_s3(file_name, 'images/original', mime_type, buffer)
                // let response = {
                //     message: "File uploaded successfully.",
                //     base_url: `${this.endpoint}/uploads/gif/${file_name}`,
                //     file_name: file_name
                // }
                // return response
            }
            else {
                let original_buffer = await this.create_sharp_file(buffer, null);
                await this.upload_file_to_s3(file_name, 'images/original', mime_type, original_buffer)

                let medium_buffer = await this.create_sharp_file(buffer, 250);
                await this.upload_file_to_s3(file_name, 'images/medium', mime_type, medium_buffer)

                let small_buffer = await this.create_sharp_file(buffer, 100);
                await this.upload_file_to_s3(file_name, 'images/small', mime_type, small_buffer)
                let response = {
                    message: "File uploaded successfully.",
                    base_url: `${this.endpoint}/uploads/images/original/${file_name}`,
                    file_name: file_name
                }
                return response
            }
        }
        catch (err) {
            console.log("upload_images....err", err);
            throw err;
        }
    }

    upload_other_files = async (file: string, folder: string, buffer: any, mime_type: string) => {
        try {
            await this.upload_file_to_s3(file, folder, mime_type, buffer)
            let response = {
                message: "File uploaded successfully.",
                base_url: `${this.endpoint}/uploads/${folder}/${file}`,
                file_name: file
            }
            return response
        }
        catch (err) {
            console.log("upload_other_files.err...", err)
            throw err;
        }
    }

    generate_video_thumbnail = async (file_url: string) => {
        return new Promise(async (resolve, reject) => {
            try {
                console.log("inside thuymbnail_FUNCTION.....");
                
                let output_file_name = "thumbnail.png";
                let output_path = path.resolve(__dirname, '../../dist/thumbnails', `${output_file_name}`);
                console.log("output_path...", output_path)
                let config = { seek: "00:00:00.00" }
                await gen_thumbnail(file_url, output_path, '500x?', config)
                    .then(async () => {
                        console.log('thumbnail generated!')
                        let thumbnail = await this.upload_thumbnail_to_s3()
                        return resolve(thumbnail)
                    })
                    .catch((err: any) => {
                        console.log("error generating thumbnail", err)
                        return resolve({})
                    })
            }
            catch (err) {
                console.log("catch err generating thubmnail", err)
                return resolve(false)
            }
        });
    }

    upload_thumbnail_to_s3 = async () => {
        try {
            console.log("  upload_thumbnail_to_s3 ........");
            let output_file_name = "thumbnail.png";
            let fetch_file = path.resolve(__dirname, '../../dist/thumbnails',`${output_file_name}`)
            let thumbnail_mime_type = mime.lookup(fetch_file);
            let thumbnail_buffer = fs.readFileSync(fetch_file);
            let thumbnail_file_name = await this.generate_file_name('thumbnail.png');
            let upload: any = await this.upload_file_to_s3(thumbnail_file_name, "thumbnails", thumbnail_mime_type, thumbnail_buffer);
            console.log("upload_thumbnail_to_s3...", upload)
            if (upload.Location != undefined) {
                fs.unlinkSync(fetch_file)
                return {
                    base_url: upload.Location,
                    file_name: thumbnail_file_name
                }
            }
        }
        catch (err) {
            console.log("upload_thumbnail_to_s3.err", err)
            throw err;
        }
    }

    upload_file_to_s3(file_name: string, folder: string, mime_type: any, buffer: any) {
        return new Promise((resolve, reject) => {
            try {
                console.log("this.bucket",this.bucket);
                
                let params = {
                    Bucket: this.bucket,
                    Key: `uploads/${folder}/${file_name}`,
                    ACL: 'public-read',
                    Body: buffer,
                    ContentType: mime_type
                }
                this.s3.upload(params, (err: any, data: any) => {
                    if (err) {
                        console.error("uploading error", err)
                        throw err
                    }
                    else {
                        console.error("uploading data.....", data)
                        return resolve(data);
                    }
                });
            }
            catch (err) {
                console.log("upload_file_to_s3_err....", err)
                return reject(err);
            }
        });
    }

}

class Upload extends Helper {

    upload_file = async (file: any) => {
        try {
            console.log("paylod_data......",file);
            
            const { originalname, buffer, mimetype } = file;
            let file_name = await this.generate_file_name(originalname)

            // ! check file type
            let mime_type = mimetype.split('/');
            let response: DTO.UploadDTO.UploadResponse;

            if (mime_type[0] == 'image') {
                response = await this.upload_images(file_name, buffer, mimetype)
            }
            else if (mime_type[0] == 'audio') {
                response = await this.upload_other_files(file_name, "audio", buffer, mimetype)
            }
            else if (mime_type[0] == 'video') {
                response = await this.upload_other_files(file_name, "video", buffer, mimetype);
                console.log("response_upload......",response);
                
                let thumbnail = await this.generate_video_thumbnail(response.base_url)
                response["thumbnail"] = thumbnail
            }
            else if (mime_type[0] == 'application' && mime_type[1] == 'pdf') {
                response = await this.upload_other_files(file_name, "document", buffer, mimetype)
            }
            else {
                response = {
                    message: 'Sorry we currently do not support this format'
                }
            }
            return response;
        }
        catch (err) {
            console.log("upload_file....err", err);
            throw err;
        }
    }

    stripe_upload_file = async (file: any) => {
        try {
            let { originalname, buffer, mimetype } = file;
            let file_name = await this.generate_file_name(originalname)
            let response = await this.upload_images(file_name, buffer, mimetype)
            return response
        }
        catch (err) {
            console.log("stripe_upload_file...err...", err)
            throw err;
        }
    }

}


@Injectable()
export class UploadService extends Mixin(Helper, Upload) {

    constructor(
        // Models: DatabaseService,
        // CommonService: CommonService,
        ConfigService: ConfigService,
        // AuthService: AuthService,
        // SharpService: SharpService
    ) {
        super( ConfigService);
    }
}
