import { Response, NextFunction } from "express";
import { Request } from "express-jwt";
import { ReturnHelper } from "../../helpers/express/return";
import Joi from "joi";
import { ILogObj, Logger } from "tslog";
import { Language } from "../../langs/lang";
import fileUpload from "express-fileupload";
import { v4 as uuidv4 } from 'uuid';
import fs from "fs";
import path from "path";

const log: Logger<ILogObj> = new Logger({ name: '[FileController]', type: 'pretty' });
const STORAGE_DIR = 'assets/saude/';

export class FileController {
    static async upload(req: Request, res: Response, next: NextFunction): Promise<Response> {
        /* #swagger.tags = ['Handle File'] 
                   #swagger.security = [{ "bearerAuth": [] }] 
                   #swagger.consumes = ['multipart/form-data'] 
                   #swagger.requestBody = { 
                       required: true, 
                       content: { 
                           "multipart/form-data": { 
                               schema: { 
                                   type: "object", 
                                   properties: { 
                                       file: { 
                                           type: "string", 
                                           format: "binary", 
                                           description: "Image file (max 1MB, jpg/png/webp)" 
                                       }, 
                                   }, 
                                   required: ["file"] 
                               } 
                           } 
                       } 
                   } 
                */
        try {
            // Check if file exists
            if (!req.files || !req.files.file) {
                return ReturnHelper.errorResponse(res, 400, 404, Language.lang.failed_insert, "File not found");
            }

            const file = req.files.file as fileUpload.UploadedFile;

            // Validate file size (max 1MB)
            const maxSize = 10 * 1024 * 1024; // 10MB
            if (file.size > maxSize) {
                return ReturnHelper.errorResponse(
                    res,
                    400,
                    400,
                    Language.lang.failed_insert,
                    "File size exceeds 10MB limit"
                );
            }

            // Validate file type
            const allowedMimes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/svg+xml', 'image/svg'];
            if (!allowedMimes.includes(file.mimetype)) {
                return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_insert, "Invalid file type. Only JPG, PNG, WEBP, and SVG are allowed");
            }
            const ext = file.name.split('.');
            const name = uuidv4() + '.' + ext[ext.length - 1];

            const result: { file: string } = {
                file: "",
            };

            const folder_path = STORAGE_DIR;
            const file_path = path.join(folder_path, name);

            // Create directory if it doesn't exist
            if (!fs.existsSync(folder_path)) {
                fs.mkdirSync(folder_path, { recursive: true });
            }

            // Move file to destination
            await file.mv(file_path);

            result.file = name;

            log.info(`File uploaded successfully: ${file_path}`);

            return ReturnHelper.successResponseAny(res, 200, Language.lang.success_insert, result);

        } catch (e: unknown) {
            // Proper error logging to avoid tslog serialization issues
            if (e instanceof Error) {
                log.error("File upload failed:", {
                    message: e.message,
                    stack: e.stack,
                    name: e.name
                });
                return ReturnHelper.errorResponse(res, 500, 500, Language.lang.failed_insert, e.message);
            } else {
                log.error("File upload failed with unknown error:", String(e));
                return ReturnHelper.errorResponse(res, 500, 500, Language.lang.failed_insert, "Unknown error occurred");
            }
        }
    }

    static async delete(req: Request, res: Response, next: NextFunction): Promise<Response> {
        /*
         #swagger.tags = ['Handle File']
         #swagger.security = [{ "bearerAuth": [] }]
         #swagger.requestBody = {
             required: true,
             content: {
                 "application/json": {
                     schema: {
                         type: "object",
                         properties: {
                             file_name: { type: "string", description: "File name to delete" }
                         },
                         required: ["file_name"]
                     }
                 }
             }
         }
         */

        try {
            const schema = Joi.object().keys({
                file_name: Joi.string().required().label('File Name'),
            });

            const param: { file_name: string } = await schema.validateAsync(req.body);

            if (param.file_name.includes('..') || param.file_name.includes('/') || param.file_name.includes('\\')) {
                return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_delete, "Invalid file name");
            }

            const file_path = path.join(STORAGE_DIR, param.file_name);

            // Check if file exists
            if (!fs.existsSync(file_path)) {
                return ReturnHelper.errorResponse(res, 404, 404, Language.lang.failed_not_found, "File not found");
            }

            // Delete file
            fs.unlinkSync(file_path);

            return ReturnHelper.successResponseAny(res, 200, Language.lang.success_delete, {});
        } catch (e: unknown) {
            log.error(e);
            const err = e as Error;

            return ReturnHelper.errorResponse(res, 500, 402, Language.lang.failed_delete, err.message);
        }
    }

    static async download(req: Request, res: Response, next: NextFunction): Promise<void | Response> {
        /*
         #swagger.tags = ['Handle File']
         #swagger.security = [{ "bearerAuth": [] }]
         #swagger.parameters['name'] = {
             in: 'query',
             required: true,
             type: 'string',
             description: 'File name'
         }
         #swagger.parameters['token'] = {
             in: 'query',
             required: true,
             type: 'string',
             description: 'Token'
         }
         */
        try {
            const schema = Joi.object({
                name: Joi.string().required().label("File Name"),
                token: Joi.string().required().label("Token"),
            });

            const param: { name: string } = await schema.validateAsync(req.query);

            if (param.name.includes('..') || param.name.includes('/') || param.name.includes('\\')) {
                return ReturnHelper.errorResponse(res, 400, 400, Language.lang.failed_not_found, "Invalid file name");
            }

            const file_path = path.join(STORAGE_DIR, param.name);

            if (!fs.existsSync(file_path)) {
                return ReturnHelper.errorResponse(res, 404, 404, Language.lang.failed_not_found, "File not found");
            }

            const buffer = fs.readFileSync(file_path);
            const fileName = param.name;
            const ext = fileName.split(".").pop()?.toLowerCase();

            const mimeMap: Record<string, string> = {
                jpg: "image/jpeg",
                jpeg: "image/jpeg",
                png: "image/png",
                webp: "image/webp",
                gif: "image/gif",
                pdf: "application/pdf",
                svg: "image/svg+xml",
            };

            const contentType = mimeMap[ext ?? ""] ?? "application/octet-stream";

            res.setHeader("Content-Type", contentType);
            res.setHeader("Content-Length", buffer.length);

            const isInline = contentType.startsWith("image/") || contentType === "application/pdf";

            if (isInline) {
                res.setHeader("Content-Disposition", `inline; filename="${encodeURIComponent(fileName)}"`);
            } else {
                res.setHeader("Content-Disposition", `attachment; filename="${encodeURIComponent(fileName)}"`);
            }

            return res.end(buffer);
        } catch (e: unknown) {
            log.error(e);
            const err = e as Error;
            return ReturnHelper.errorResponse(res, 400, 401, Language.lang.failed_not_found, err.message);
        }
    }
}