const express = require('express');
const fs = require('fs');
const path = require('path');
const multer = require('multer');
const models = require('../models/index.js');
const chkLogin = require('../middleware/auth.js');
const resp = require('../response.js');

const entityHeaderRouter = express.Router();

const uploadDir = path.join(__dirname, '..', 'uploads', 'entity-headers');
if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir, { recursive: true });
}

const allowedMimeTypes = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/gif']);
const allowedHeaderTypes = new Set(['logo_only', 'logo_entity_name', 'logo_custom_text', 'entity_name_only', 'custom_text_only']);

const storage = multer.diskStorage({
    destination: (_req, _file, cb) => cb(null, uploadDir),
    filename: (_req, file, cb) => {
        const ext = path.extname(file.originalname || '').toLowerCase();
        const safeExt = ext || '.png';
        cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${safeExt}`);
    }
});

const upload = multer({
    storage,
    limits: { fileSize: 5 * 1024 * 1024 },
    fileFilter: (_req, file, cb) => {
        if (!allowedMimeTypes.has(file.mimetype)) {
            return cb(new Error('Unsupported image type'));
        }
        return cb(null, true);
    }
});

entityHeaderRouter.use(chkLogin);

const isSessionAdmin = (req) => {
    const acl = req?.session?.userAcl;
    if (!Array.isArray(acl)) return false;
    return acl.some((a) => a.name === 'User' && !!a?.GroupModules?.update);
};

const denyAdminMutation = (req, res) => {
    if (!isSessionAdmin(req)) return false;
    res.status(403).json(
        resp(
            'Access Denied',
            'Admin users are not allowed to create, update, or delete headers.',
            null,
            req,
            false
        )
    );
    return true;
};

const getAllowedEntityIds = (req) => {
    const sessionEntities = req?.session?.entities;
    if (!Array.isArray(sessionEntities)) return [];
    return sessionEntities
        .map((e) => Number(e?.id))
        .filter((id) => Number.isInteger(id) && id > 0);
};

const canAccessEntity = (req, entId) => getAllowedEntityIds(req).includes(Number(entId));

const isMissingEntityHeaderTableError = (err) => {
    const code = err?.original?.code || err?.parent?.code;
    const sqlMessage = String(err?.original?.sqlMessage || err?.parent?.sqlMessage || '');
    return code === 'ER_NO_SUCH_TABLE' && sqlMessage.includes('entity_headers');
};

const headerTypeNeedsLogo = (headerType) => ['logo_only', 'logo_entity_name', 'logo_custom_text'].includes(headerType);

const headerTypeNeedsCustomText = (headerType) => ['logo_custom_text', 'custom_text_only'].includes(headerType);

const getStoredHeaderFilename = (header) => {
    if (!header) return null;
    if (header.logo_filename) return path.basename(String(header.logo_filename));
    if (header.logo_path) return path.basename(String(header.logo_path));
    return null;
};

const safeDeleteUploadedFile = (filename) => {
    if (!filename) return;
    try {
        const absoluteFile = path.join(uploadDir, path.basename(filename));
        if (fs.existsSync(absoluteFile)) {
            fs.unlinkSync(absoluteFile);
        }
    } catch (_err) {
        // Keep API flow resilient even if filesystem cleanup fails.
    }
};

const cleanupRequestFile = (file) => {
    if (!file?.filename) return;
    safeDeleteUploadedFile(file.filename);
};

const normalizeHeader = (row) => {
    if (!row) return null;
    const data = row.toJSON ? row.toJSON() : row;
    const logoUrl = data.logo_path || (data.logo_filename ? `/uploads/entity-headers/${path.basename(String(data.logo_filename))}` : null);
    return {
        id: data.id,
        ent_id: data.ent_id,
        header_type: data.header_type,
        custom_text: data.custom_text,
        logo_url: logoUrl,
        logo_path: data.logo_path,
        createdAt: data.createdAt,
        updatedAt: data.updatedAt
    };
};

entityHeaderRouter.get('/readheaders', async (req, res) => {
    try {
        const entityIds = getAllowedEntityIds(req);
        if (!entityIds.length) {
            return res.json(resp('Entity Headers', 'No entities assigned to this user', [], req));
        }

        const entities = await models.Entity.findAll({
            where: { id: entityIds },
            attributes: ['id', 'name'],
            include: [
                {
                    model: models.EntityHeader,
                    as: 'entity_header',
                    required: false,
                    attributes: ['id', 'ent_id', 'header_type', 'custom_text', 'logo_path', 'createdAt', 'updatedAt']
                }
            ],
            order: [['name', 'ASC']]
        });

        const rows = entities.map((entity) => {
            const plain = entity.toJSON();
            return {
                entity_id: plain.id,
                entity_name: plain.name,
                header: normalizeHeader(plain.entity_header)
            };
        });

        return res.json(resp('Entity Headers', 'Entity headers returned successfully', rows, req));
    } catch (err) {
        if (isMissingEntityHeaderTableError(err)) {
            const entityIds = getAllowedEntityIds(req);
            if (!entityIds.length) {
                return res.json(resp('Entity Headers', 'No entities assigned to this user', [], req));
            }

            const entities = await models.Entity.findAll({
                where: { id: entityIds },
                attributes: ['id', 'name'],
                order: [['name', 'ASC']]
            });

            const rows = entities.map((entity) => {
                const plain = entity.toJSON ? entity.toJSON() : entity;
                return {
                    entity_id: plain.id,
                    entity_name: plain.name,
                    header: null
                };
            });

            return res.json(resp('Entity Headers', 'Entity headers table is not initialized yet', rows, req));
        }

        return res.status(500).json(resp('Error', 'Error returning entity headers', err, req, false));
    }
});

entityHeaderRouter.post('/readheaderrow', async (req, res) => {
    try {
        const entId = Number(req.body?.ent_id);
        if (!Number.isInteger(entId) || entId <= 0) {
            return res.status(400).json(resp('Error', 'Valid entity id is required', null, req, false));
        }

        if (!canAccessEntity(req, entId)) {
            return res.status(403).json(resp('Access Denied', 'Entity access denied', null, req, false));
        }

        const header = await models.EntityHeader.findOne({ where: { ent_id: entId } });
        return res.json(resp('Entity Header', 'Entity header returned successfully', normalizeHeader(header), req));
    } catch (err) {
        if (isMissingEntityHeaderTableError(err)) {
            return res.json(resp('Entity Header', 'Entity headers table is not initialized yet', null, req));
        }

        return res.status(500).json(resp('Error', 'Error returning entity header', err, req, false));
    }
});

entityHeaderRouter.post('/createheader', upload.single('logo'), async (req, res) => {
    try {
        if (denyAdminMutation(req, res)) return;

        const entId = Number(req.body?.ent_id);
        const headerType = String(req.body?.header_type || '').trim();
        const customText = req.body?.custom_text ? String(req.body.custom_text).trim() : null;

        if (!Number.isInteger(entId) || entId <= 0) {
            return res.status(400).json(resp('Error', 'Valid entity id is required', null, req, false));
        }

        if (!canAccessEntity(req, entId)) {
            return res.status(403).json(resp('Access Denied', 'Entity access denied', null, req, false));
        }

        if (!allowedHeaderTypes.has(headerType)) {
            return res.status(400).json(resp('Error', 'Invalid header type', null, req, false));
        }

        if (headerType === 'logo_custom_text' && !customText) {
            return res.status(400).json(resp('Error', 'Custom text is required for this header type', null, req, false));
        }

        if (headerType === 'custom_text_only' && !customText) {
            return res.status(400).json(resp('Error', 'Custom text is required for this header type', null, req, false));
        }

        if (headerTypeNeedsLogo(headerType) && !req.file) {
            return res.status(400).json(resp('Error', 'Logo image is required', null, req, false));
        }

        const existing = await models.EntityHeader.findOne({ where: { ent_id: entId } });
        if (existing) {
            cleanupRequestFile(req.file);
            return res.status(409).json(resp('Error', 'Only one header is allowed per entity', null, req, false));
        }

        const created = await models.EntityHeader.create({
            ent_id: entId,
            header_type: headerType,
            custom_text: headerTypeNeedsCustomText(headerType) ? customText : null,
            logo_filename: req.file ? req.file.filename : null,
            logo_path: req.file ? `/uploads/entity-headers/${req.file.filename}` : null,
            mime_type: req.file ? req.file.mimetype : null,
            creatorId: req.session.userId
        });

        return res.json(resp('Header Created', 'Entity header created successfully', normalizeHeader(created), req));
    } catch (err) {
        cleanupRequestFile(req.file);
        return res.status(500).json(resp('Error', 'Error creating entity header', err, req, false));
    }
});

entityHeaderRouter.put('/updateheader', upload.single('logo'), async (req, res) => {
    try {
        if (denyAdminMutation(req, res)) return;

        const entId = Number(req.body?.ent_id);
        const headerType = String(req.body?.header_type || '').trim();
        const customText = req.body?.custom_text ? String(req.body.custom_text).trim() : null;

        if (!Number.isInteger(entId) || entId <= 0) {
            return res.status(400).json(resp('Error', 'Valid entity id is required', null, req, false));
        }

        if (!canAccessEntity(req, entId)) {
            return res.status(403).json(resp('Access Denied', 'Entity access denied', null, req, false));
        }

        if (!allowedHeaderTypes.has(headerType)) {
            return res.status(400).json(resp('Error', 'Invalid header type', null, req, false));
        }

        if (headerType === 'logo_custom_text' && !customText) {
            return res.status(400).json(resp('Error', 'Custom text is required for this header type', null, req, false));
        }

        if (headerType === 'custom_text_only' && !customText) {
            return res.status(400).json(resp('Error', 'Custom text is required for this header type', null, req, false));
        }

        const header = await models.EntityHeader.findOne({ where: { ent_id: entId } });
        if (!header) {
            return res.status(404).json(resp('Error', 'Header not found for selected entity', null, req, false));
        }

        const updateData = {
            header_type: headerType,
            custom_text: headerTypeNeedsCustomText(headerType) ? customText : null
        };

        if (req.file) {
            const previousFilename = getStoredHeaderFilename(header);
            updateData.logo_filename = req.file.filename;
            updateData.logo_path = `/uploads/entity-headers/${req.file.filename}`;
            updateData.mime_type = req.file.mimetype;
            safeDeleteUploadedFile(previousFilename);
        } else if (!headerTypeNeedsLogo(headerType)) {
            updateData.logo_filename = null;
            updateData.logo_path = null;
            updateData.mime_type = null;
        }

        await models.EntityHeader.update(updateData, { where: { id: header.id } });
        const updated = await models.EntityHeader.findByPk(header.id);
        return res.json(resp('Header Updated', 'Entity header updated successfully', normalizeHeader(updated), req));
    } catch (err) {
        cleanupRequestFile(req.file);
        return res.status(500).json(resp('Error', 'Error updating entity header', err, req, false));
    }
});

entityHeaderRouter.delete('/deleteheader', async (req, res) => {
    try {
        if (denyAdminMutation(req, res)) return;

        const entId = Number(req.body?.ent_id);
        if (!Number.isInteger(entId) || entId <= 0) {
            return res.status(400).json(resp('Error', 'Valid entity id is required', null, req, false));
        }

        if (!canAccessEntity(req, entId)) {
            return res.status(403).json(resp('Access Denied', 'Entity access denied', null, req, false));
        }

        const header = await models.EntityHeader.findOne({ where: { ent_id: entId } });
        if (!header) {
            return res.status(404).json(resp('Error', 'Header not found for selected entity', null, req, false));
        }

        const storedFilename = getStoredHeaderFilename(header);

        await models.EntityHeader.destroy({ where: { id: header.id } });
        safeDeleteUploadedFile(storedFilename);
        return res.json(resp('Header Deleted', 'Entity header deleted successfully', { ent_id: entId }, req));
    } catch (err) {
        return res.status(500).json(resp('Error', 'Error deleting entity header', err, req, false));
    }
});

entityHeaderRouter.get('/image/:id', async (req, res) => {
    try {
        const id = Number(req.params?.id);
        if (!Number.isInteger(id) || id <= 0) {
            return res.status(400).json(resp('Error', 'Invalid header id', null, req, false));
        }

        const header = await models.EntityHeader.findByPk(id);
        if (!header) {
            return res.status(404).json(resp('Error', 'Header not found', null, req, false));
        }

        if (!canAccessEntity(req, header.ent_id)) {
            return res.status(403).json(resp('Access Denied', 'Entity access denied', null, req, false));
        }

        const filePath = path.join(uploadDir, path.basename(header.logo_filename || ''));
        if (!header.logo_filename || !fs.existsSync(filePath)) {
            return res.status(404).json(resp('Error', 'Image not found', null, req, false));
        }

        res.type(header.mime_type || 'application/octet-stream');
        return res.sendFile(filePath);
    } catch (err) {
        return res.status(500).json(resp('Error', 'Error serving header image', err, req, false));
    }
});

entityHeaderRouter.use((err, req, res, next) => {
    if (!err) return next();

    if (err instanceof multer.MulterError) {
        if (err.code === 'LIMIT_FILE_SIZE') {
            return res.status(400).json(resp('Error', 'Image file is too large (max 5MB)', null, req, false));
        }
        return res.status(400).json(resp('Error', 'Invalid upload payload', null, req, false));
    }

    if (String(err.message || '').includes('Unsupported image type')) {
        return res.status(400).json(resp('Error', 'Unsupported image type. Use PNG, JPG, WEBP, or GIF.', null, req, false));
    }

    return res.status(500).json(resp('Error', 'Unexpected upload error', null, req, false));
});

module.exports = entityHeaderRouter;
