import { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi'
import { AppError } from "../../utils/errors"
import { Common } from "../../utils/common"
import { AttachmentService } from "../services/attachment.service"
import * as extensions from '../config/extensions';
import { Readable } from 'stream';

export class AttachmentHandler {
    // set the class variables
    private getAttachmentServiceObject = async (request: Request) => {
        let variables = await Common.getVariables(request)
        let attachmentService = new AttachmentService(variables);
        return attachmentService;
    }

    upload = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let attachmentService = await this.getAttachmentServiceObject(request);
            const payload = request.payload as any;
            const uploadFileInput: AttachmentUploadServiceInput = {
                file: payload.file,
                isGlobal: payload.isGlobal,
            };
            let fileData: attachmentObjectInteface = await attachmentService.uploadFile(uploadFileInput);
            let responseData = { message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: fileData }
            return h.response(responseData).code(200)
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }
    streamToBuffer = (stream: Readable): Promise<Buffer> =>
        new Promise((resolve, reject) => {
            const chunks: Buffer[] = [];
            stream.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));
            stream.once('end', () => resolve(Buffer.concat(chunks)));
            stream.once('error', reject);
        });

    // view file
    view = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let attachmentService = await this.getAttachmentServiceObject(request);
            let { id, uniqueName } = request.params as any;
            let { size, format } = request.query as any;
            const getFileByUniqueIdentifierInput: AttachmentGetFileByUniqueIdentifierServiceInput = {
                id: id ? +id : null,
                uniqueName: uniqueName ?? null,
                size: size ?? null,
                format: format ?? null,
            };
            let viewData: boolean | fileStreamData = await attachmentService.getFileByUniqueIdentifier(getFileByUniqueIdentifierInput);
            if (typeof viewData !== 'boolean') {
                const contentType = extensions.getContentType(viewData.attachment.extension);
                if (viewData.attachment.uniqueName.match(/.(mp4|avi|mp|m4a|MOV|svg|webp)$/i)) {
                    const isSwaggerRequest =
                        (request.headers['user-agent'] || '').toLowerCase().includes('swagger')
                        || (request.headers['accept'] || '').includes('application/json');
                    return h.response(viewData.streamData)
                        .header('Content-Type', contentType)
                        .header('Content-Disposition', `${isSwaggerRequest ? 'attachment' : 'inline'}; filename=` + viewData.attachment.uniqueName)
                        .header('Content-Transfer-Encoding', 'chunked')
                        .header('accept-ranges', 'bytes')
                        .header('content-length', viewData.attachment.size.toString())
                }
                // ✅ Render JSON and TXT inline
                else if (viewData.attachment.uniqueName.match(/\.(json|txt)$/i)) {
                    const fileBuffer = await this.streamToBuffer(viewData.streamData);
                    return h.response(fileBuffer)
                        .header('Content-Type', contentType)
                        .header('Content-Disposition', 'inline; filename=' + viewData.attachment.uniqueName);
                }
                else if (!viewData.attachment.uniqueName.match(/.(jpg|jpeg|png|gif)$/i)) {
                    return h.response(viewData.streamData)
                        .header('Content-Type', contentType)
                        .header('Content-Disposition', 'attachment; filename=' + viewData.attachment.uniqueName);
                }
                return h.response(viewData.streamData)
                    .header('Content-Type', contentType)
            }
            throw new AppError(400, 'SOMETHING_WENT_WRONG_IN_SERVICE', {});
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // get all attachment
    list = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let attachmentService = await this.getAttachmentServiceObject(request);
            const listRequest = request.query as AttachmentListRequestObject;
            const getAllFileInfoInput: AttachmentGetAllFileInfoServiceInput = { listRequest };
            let getAllFile: AttachmentListRequestObject = await attachmentService.getAllFileInfo(getAllFileInfoInput);
            if (getAllFile) {
                return h.response({ message: 'REQUEST_PROCESSED_SUCCESSFULLY', responseData: getAllFile }).code(200);
            }
            throw new AppError(400, 'SOMETHING_WENT_WRONG_IN_SERVICE', {});
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // download file
    download = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let attachmentService = await this.getAttachmentServiceObject(request);
            let { id, uniqueName } = request.params as any;
            let { size } = request.query as any;
            const getFileByUniqueIdentifierInput: AttachmentGetFileByUniqueIdentifierServiceInput = {
                id: id ? +id : null,
                uniqueName: uniqueName ?? null,
                size: size ?? null,
                format: null,
            };
            let viewData: boolean | fileStreamData = await attachmentService.getFileByUniqueIdentifier(getFileByUniqueIdentifierInput);
            if (typeof viewData !== 'boolean') {
                const contentType = extensions.getContentType(viewData.attachment.extension);
                return h.response(viewData.streamData)
                    .header('Content-Type', contentType)
                    .header('Content-Disposition', 'attachment; filename=' + viewData.attachment.uniqueName);
            }
            throw new AppError(400, 'SOMETHING_WENT_WRONG_IN_SERVICE', {});
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // delete file
    delete = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let attachmentService = await this.getAttachmentServiceObject(request);
            let { id, uniqueName } = request.params as any;
            const authUserData = request.auth.isAuthenticated && (request.auth.credentials as any).userData
                ? (request.auth.credentials as any).userData as AuthCredentials
                : false;
            const deleteAttachmentInput: AttachmentDeleteServiceInput = {
                id: id ? +id : null,
                uniqueName: uniqueName ?? null,
                authUserData,
            };
            await attachmentService.deleteAttachment(deleteAttachmentInput);
            const responseData = {
                message: 'REQUEST_PROCESSED_SUCCESSFULLY',
                responseData: {},
            };
            return h.response(responseData).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }
}
