const { logger } = require("nodejs-log-agent");
const { sendError } = require("../libs/errorCapture");
const { generatePDF } = require("../libs/pdfGenerate");
const { getContent } = require("../libs/template");
const { getInvoice } = require("../libs/whmcs");
const db = require("../models");
const { Op } = require("sequelize");
const { sendnotif } = require("../sockets/socket");
const moment = require('moment')
require('moment/locale/id') // load locale Indonesia
moment.locale('id') // set locale default ke Indonesia
const path = require('path');
const fs = require('fs').promises;
const ejs = require('ejs');
const puppeteer = require('puppeteer');
const MarkdownIt = require('markdown-it');
const fsSync = require('fs'); // Menggunakan fs sinkron untuk membaca file changelog

const previewInvoice = async (req, res) => {
    const { inv_id, name, phoneNumber, address, company } = req.body;

    if (!inv_id) {
        return res.status(400).json({
            status: false,
            message: "Invoice ID (inv_id) is required for preview."
        });
    }

    try {
        // Panggil WHMCS untuk mendapatkan data invoice
        const datawhmcs = await getInvoice(inv_id, name, phoneNumber, address, company);

        // Ekstrak informasi klien untuk respons yang lebih jelas
        const clientInfo = {
            name: datawhmcs.client_name || '',
            phone: datawhmcs.client_phone || '',
            address: datawhmcs.client_address || '',
            company: datawhmcs.company || ''
        };

        logger.info(`[Preview] Successfully fetched data for invoice ${inv_id}`);
        // Kirim data kembali ke FE untuk ditampilkan, tanpa menyimpan
        return res.json({
            status: true,
            message: "Invoice data fetched successfully for preview.",
            clientInfo: clientInfo,
            invoiceData: datawhmcs
        });
    } catch (error) {
        logger.error(`[Preview] Error fetching invoice ${inv_id}`, { message: error.message, stack: error.stack });
        sendError(error.message);
        return res.status(500).json({ status: false, message: error.message, error });
    }
};
const create = async (req, res) => {
    const { inv_id, type, name, phoneNumber, address, company } = req.body;

    try {
        const datawhmcs = await getInvoice(inv_id, name, phoneNumber, address, company);
        const getcontent = await getContent(type, datawhmcs);
        
        // Save Invoice
        const invoice = await saveInvoice(inv_id, type, datawhmcs, getcontent);
        if (!invoice) throw new Error("cannot save invoice");
        
        if(!type.includes('reminder') && !type.includes('overdue')){
            // Save Invoice Details
            const invoiceDetails = await saveInvoiceDetails(datawhmcs);
            if (!invoiceDetails) throw new Error("cannot save invoice details");
    
            // Save Invoice Items
            const invoiceItem = await saveInvoiceItems(inv_id, datawhmcs.items);
            if (!invoiceItem) throw new Error("cannot save invoice items");
    
            // Save Invoice Transactions (if available)
            if (datawhmcs.transaction) {
                const invoiceTrans = await saveInvoiceTransactions(inv_id, datawhmcs.transaction);
                if (!invoiceTrans) throw new Error("cannot save invoice transactions");
            }
    
            // Generate Invoice PDF
            const generateinvoice = await handlePDFGeneration(datawhmcs);
            if (!generateinvoice.status) throw new Error("cannot generate invoice pdf");
        }
        logger.info("Create Invoice Success", { message: `Invoice ${inv_id} created successfully` });
        return res.json({
            status: true,
            message: "Invoice created successfully"
        });
    } catch (error) {
        logger.error("Create Invoice Error", { message : error.message , stack : error.stack, error : error });
        sendError(error.message);
        res.json({
            status: false,
            message: error.message,
            error
        });
    }
};

const createManualInvoice = async (req, res) => {
    // Data invoice lengkap sekarang diharapkan dari body request, bukan dari WHMCS lagi.
    const { type, inv_id } = req.body; // Expect full invoiceData

    if (!type || !inv_id) { // Check for invoiceData and its inv_id
        return res.status(400).json({
            status: false,
            message: "Request body must contain 'type' and 'invoiceData' (with inv_id)."
        });
    }

    try {
        // Do NOT call getInvoice from WHMCS here. Use the provided invoiceData.
        const datawhmcs = await getInvoice(inv_id);
        const getcontent = await getContent(type, datawhmcs);
        // Save Invoice
        const invoice = await saveInvoice(datawhmcs.inv_id, type, datawhmcs, getcontent);
        if (!invoice) throw new Error("cannot save invoice");

        if (!type.includes('reminder') && !type.includes('overdue')) {
            // Save Invoice Details
            const invoiceDetails = await saveInvoiceDetails(datawhmcs);
            if (!invoiceDetails) throw new Error("cannot save invoice details");

            // Save Invoice Items
            const invoiceItem = await saveInvoiceItems(datawhmcs.inv_id, datawhmcs.items);
            if (!invoiceItem) throw new Error("cannot save invoice items");

            // Save Invoice Transactions (if available)
            if (datawhmcs.transaction && datawhmcs.transaction.length > 0) {
                const invoiceTrans = await saveInvoiceTransactions(datawhmcs.inv_id, datawhmcs.transaction);
                if (!invoiceTrans) throw new Error("cannot save invoice transactions");
            }

            // Generate Invoice PDF
            const generateinvoice = await handlePDFGeneration(datawhmcs);
            if (!generateinvoice.status) throw new Error("cannot generate invoice pdf");
        }
        logger.info("[Manual Create] Invoice Success", { message: `Invoice ${datawhmcs.inv_id} created successfully` });
        return res.json({ status: true, message: "Invoice created successfully" });
    } catch (error) {
        logger.error("[Manual Create] Invoice Error", { message: error.message, stack: error.stack, error: error });
        sendError(error.message);
        return res.status(500).json({ status: false, message: error.message, error });
    }
};

const getUnsendedInvoices = async (req, res) => {
    try {
        const invoices = await db.invoice.findAll({
            where: {
                delivered: false
            }
        });
        return res.json({
            status :true,
            message : "success",
            data : invoices
        });
    } catch (error) {
        logger.error("function get Unsended Invoices", { message : error.message , stack : error.stack, error : error });
        res.json({
            status: false,
            message: error.message,
            error
        });
    }
}
const getInvoices = async (req, res) => {
    try {
        const invoices = await db.invoice.findAll({
            order: [
                ['updatedAt', 'DESC'],
            ],
        });
        return res.json({
            status :true,
            message : "success",
            data : invoices
        });
    } catch (error) {
        logger.error("function get Invoices", { message : error.message , stack : error.stack, error : error });
        sendError(error.message);
        res.json({
            status: false,
            message: error.message,
            error
        });
    }
}

const skipInvoice = async (req, res) => {
    const { inv_id } = req.params;
    const skippedBy = req.userId ? String(req.userId) : 'admin';

    if (!inv_id) {
        return res.status(400).json({ status: false, message: "Invoice ID is required" });
    }

    try {
        const [updatedRows] = await db.invoice.update(
            {
                delivered: true,
                skipped_at: new Date(),
                skip_reason: 'manual',
                skipped_by: skippedBy
            },
            { where: { inv_id } }
        );

        if (updatedRows > 0) {
            logger.info(`[Skip] Invoice ${inv_id} has been manually skipped by ${skippedBy}.`);
            return res.json({ status: true, message: `Invoice ${inv_id} skipped successfully.` });
        } else {
            logger.warn(`[Skip] Invoice ${inv_id} not found or already delivered.`);
            return res.status(404).json({ status: false, message: `Invoice ${inv_id} not found or no update was needed.` });
        }
    } catch (error) {
        logger.error(`[Skip] Error skipping invoice ${inv_id}`, { message: error.message, stack: error.stack, error: error });
        sendError(error.message);
        return res.status(500).json({ status: false, message: "Internal server error", error: error.message });
    }
};

const skipInvoicesBatch = async (req, res) => {
    const { invoiceIds } = req.body;
    const skippedBy = req.userId ? String(req.userId) : 'admin';

    if (!invoiceIds || !Array.isArray(invoiceIds) || invoiceIds.length === 0) {
        return res.status(400).json({ status: false, message: "A non-empty array of invoiceIds is required." });
    }
    try {
        const [updatedRows] = await db.invoice.update(
            {
                delivered: true,
                skipped_at: new Date(),
                skip_reason: 'manual',
                skipped_by: skippedBy
            },
            { where: { inv_id: { [Op.in]: invoiceIds } } }
        );

        logger.info(`[Skip] Batch skip executed for ${invoiceIds.length} invoices. ${updatedRows} were updated.`);
        return res.json({
            status: true,
            message: `Batch skip successful. ${updatedRows} invoices were updated.`,
            data: {
                requested: invoiceIds.length,
                updated: updatedRows
            }
        });
    } catch (error) {
        logger.error(`[Skip] Error during batch skipping invoices`, { message: error.message, stack: error.stack, error: error });
        sendError(error.message);
        return res.status(500).json({ status: false, message: "Internal server error", error: error.message });
    }
};

const requeueInvoice = async (req, res) => {
    const { inv_id } = req.params;

    if (!inv_id) {
        return res.status(400).json({ status: false, message: "Invoice ID is required" });
    }

    try {
        const [updatedRows] = await db.invoice.update(
            {
                delivered: false,
                skipped_at: null,
                skip_reason: null,
                skipped_by: null
            },
            { where: { inv_id } }
        );

        if (updatedRows > 0) {
            logger.info(`[Requeue] Invoice ${inv_id} has been re-queued for sending.`);
            return res.json({ status: true, message: `Invoice ${inv_id} re-queued successfully.` });
        } else {
            logger.warn(`[Requeue] Invoice ${inv_id} not found or already marked for sending.`);
            return res.status(404).json({ status: false, message: `Invoice ${inv_id} not found or no update was needed.` });
        }
    } catch (error) {
        logger.error(`[Requeue] Error re-queuing invoice ${inv_id}`, { message: error.message, stack: error.stack, error: error });
        sendError(error.message);
        return res.status(500).json({ status: false, message: "Internal server error", error: error.message });
    }
};

const requeueInvoicesBatch = async (req, res) => {
    const { invoiceIds } = req.body;

    if (!invoiceIds || !Array.isArray(invoiceIds) || invoiceIds.length === 0) {
        return res.status(400).json({ status: false, message: "A non-empty array of invoiceIds is required." });
    }

    try {
        const [updatedRows] = await db.invoice.update(
            {
                delivered: false,
                skipped_at: null,
                skip_reason: null,
                skipped_by: null
            },
            { where: { inv_id: { [Op.in]: invoiceIds } } }
        );

        logger.info(`[Requeue] Batch re-queue executed for ${invoiceIds.length} invoices. ${updatedRows} were updated.`);
        return res.json({
            status: true,
            message: `Batch re-queue successful. ${updatedRows} invoices were marked for re-sending.`,
            data: { requested: invoiceIds.length, updated: updatedRows }
        });
    } catch (error) {
        logger.error(`[Requeue] Error during batch re-queuing invoices`, { message: error.message, stack: error.stack, error: error });
        sendError(error.message);
        return res.status(500).json({ status: false, message: "Internal server error", error: error.message });
    }
};

// ====== SKIPPED INVOICES ======
const getSkippedInvoices = async (req, res) => {
    try {
        const invoices = await db.invoice.findAll({
            where: {
                skip_reason: { [Op.not]: null }
            },
            order: [['skipped_at', 'DESC']]
        });

        const today = new Date();
        today.setHours(0, 0, 0, 0);

        const summary = {
            total: invoices.length,
            today: invoices.filter(inv => inv.skipped_at && new Date(inv.skipped_at) >= today).length,
            auto: invoices.filter(inv => inv.skip_reason === 'auto_expired').length,
            manual: invoices.filter(inv => inv.skip_reason === 'manual').length,
            phoneError: invoices.filter(inv => inv.skip_reason === 'phone_error').length,
        };

        return res.json({
            status: true,
            message: 'success',
            summary,
            data: invoices
        });
    } catch (error) {
        logger.error('[Skipped] Error fetching skipped invoices', { message: error.message, stack: error.stack });
        sendError(error.message);
        return res.status(500).json({ status: false, message: error.message });
    }
};
const formatInvoiceResponse = (datawhmcs) => {
  // Provide safe defaults for all potentially missing data
  const details = datawhmcs.invoiceDetails?.dataValues || {};
  const items = datawhmcs.invoiceitem || [];
  const transactions = datawhmcs.invoiceTrans || [];

  return {
    // Core invoice data with fallbacks
    inv_id: datawhmcs.inv_id,
    inv_status: datawhmcs.inv_status,
    inv_date: details.inv_date
      ? moment(details.inv_date).format('dddd, DD MMMM YYYY')
      : '',
    inv_duedate: details.inv_duedate
      ? moment(details.inv_duedate).format('dddd, DD MMMM YYYY')
      : '',

    // Client details with fallbacks
    client_name: details.client_name || datawhmcs.inv_name || 'N/A',
    client_phone: datawhmcs.phoneNumber || '',
    client_address: details.client_address || '',
    company: details.company || '',

    // Financial details
    sub_total: details.sub_total || '0',
    total: details.total || '0',
    ppn: details.ppn || '0',
    credit: details.credit || '0',
    balance: details.total || '0',

    // Static company info
    company: {
      name: 'PT. Semut Data Indonesia',
      address: 'Ruko Amparan Jati Blok A No.06, Margajaya, Bogor Barat',
      phone: '0251 8471670',
      whatsapp: '+62 811 1185 911'
    },

    // Safely map items and transactions, ensuring they are always arrays
    items: Array.isArray(items) ? items.map(item => ({
      description: item.description || '',
      amount: item.total || '0'
    })) : [],
    transaction: Array.isArray(transactions) ? transactions.map(trx => ({
      gateway: trx.gateway === 'banktransfer' ? 'Bank Transfer' : (trx.gateway || ''),
      date: trx.date ? moment(trx.date).format('dddd, DD MMMM YYYY') : '',
      description: trx.description || '',
      amount: trx.amount || '0'
    })) : []
  }
}

const debugInvoiceHtml = async (req, res) => {
    const { inv_id } = req.params;
    let browser = null;

    try {
        // 1. Ambil data invoice
        const invoice = await db.invoice.findOne({
            where: { inv_id },
            include: [
                { model: db.invoicedetails, as: 'invoiceDetails' },
                { model: db.invoiceitem, as: 'invoiceitem' },
                { model: db.invoice_trans, as: 'invoiceTrans' }
            ]
        });

        if (!invoice) {
            return res.status(404).json({ status: false, message: `Invoice with ID ${inv_id} not found.` });
        }

        // 2. Format data dan render template EJS menjadi HTML
        const formattedData = formatInvoiceResponse(invoice);
        const templatePath = path.join(__dirname, `../views/invoice.ejs`);
        const htmlContent = await ejs.renderFile(templatePath, formattedData);

        // 3. Gunakan Puppeteer untuk mengambil screenshot
        logger.info(`[Debug Screenshot] Launching Puppeteer for invoice ${inv_id}`);
        browser = await puppeteer.launch({
            headless: true,
            args: ['--no-sandbox', '--disable-setuid-sandbox']
        });
        const page = await browser.newPage();
        
        // Atur konten halaman dengan HTML yang sudah dirender
        await page.setContent(htmlContent, { waitUntil: 'networkidle0' });

        // Ambil screenshot dari elemen invoice-box
        const element = await page.$('.invoice-box');
        const screenshotBuffer = await element.screenshot();

        await browser.close();
        browser = null;
        logger.info(`[Debug Screenshot] Screenshot generated for invoice ${inv_id}`);

        // 4. Kirim screenshot sebagai response
        res.setHeader('Content-Type', 'image/png');
        return res.send(screenshotBuffer);

    } catch (error) {
        logger.error(`[Debug HTML] Error generating HTML for invoice ${inv_id}`, { 
            message: error.message, 
            stack: error.stack, 
            error: error 
        });
        return res.status(500).send(`
            <h1>Error 500: Internal Server Error</h1>
            <p>Failed to generate HTML for invoice ${inv_id}.</p>
            <pre>${error.stack}</pre>
        `);
    } finally {
        if (browser) {
            await browser.close();
        }
    }
};
// Helper function to save the invoice
const saveInvoice = async (inv_id, type, datawhmcs, content) => {
    let data = {
        inv_id,
        inv_status: datawhmcs.inv_status,
        inv_name: datawhmcs.client_name,
        phoneNumber: datawhmcs.client_phone,
        inv_type: type,
        content,
        delivered: false,
        pdfCreated: false,
        pdfName: "",
        isFile: true
    }
    if(type.includes('reminder') || type.includes('overdue')){
        data.pdfCreated = true,
        data.isFile = false
    }
    return await db.invoice.upsert(data);
};

// Helper function to save invoice details
const saveInvoiceDetails = async (datawhmcs) => {
    return await db.invoicedetails.upsert(datawhmcs);
};

// Helper function to save invoice items
const saveInvoiceItems = async (inv_id, items) => {
    const formattedItems = items.map((item) => ({
        id: item.id,
        inv_id,
        description: item.description,
        total: item.amount
    }));

    return await db.invoiceitem.bulkCreate(formattedItems, {
        updateOnDuplicate: ['description', 'total']
    });
};

// Helper function to save invoice transactions
const saveInvoiceTransactions = async (inv_id, transactions) => {
    const formattedTransactions = transactions.map((transaction) => ({
        id: transaction.id,
        inv_id,
        gateway: transaction.gateway,
        date: transaction.date,
        description: transaction.description,
        amount: transaction.amountin,
        balance: transaction.amountout
    }));

    return await db.invoice_trans.bulkCreate(formattedTransactions, {
        updateOnDuplicate: ['gateway', 'date', 'description', 'amount', 'balance']
    });
};

// Helper function to generate PDF
const handlePDFGeneration = async (datawhmcs) => {
    // Gunakan tanggal invoice (inv_date) sebagai dasar untuk struktur folder
    const invoiceDate = moment(datawhmcs.inv_date); // Pastikan datawhmcs.inv_date ada dan dalam format yang bisa dibaca moment.js
    const year = invoiceDate.format('YYYY');
    const month = invoiceDate.format('MM');
    const day = invoiceDate.format('DD');
    
    // Construct the target directory relative to the project root
    const targetBaseDir = path.join(__dirname, '../../public/invoice', year, month, day);
    // This is the path that will be stored in the database (relative to 'public')
    const relativePdfPathPrefix = path.join('/invoice', year, month, day); 

    // Ensure the directory exists
    await fs.mkdir(targetBaseDir, { recursive: true });

    const generateinvoice = await generatePDF(datawhmcs, targetBaseDir, relativePdfPathPrefix);
    if (generateinvoice.status) {
        await db.invoice.update({ pdfCreated: true, pdfName: generateinvoice.pdfPath }, { where: { inv_id: datawhmcs.inv_id } });
    }
    return generateinvoice;
};

// ====== PDF FILE MANAGER CONTROLLERS ======
const getPDFFiles = async (req, res) => {
    try {
        const invoicesWithPdf = await db.invoice.findAll({
            where: {
                pdfCreated: true,
                pdfName: {
                    [Op.ne]: null // pdfName is not null
                }
            },
            attributes: ['inv_id', 'inv_name', 'pdfName', 'updatedAt'], // Get relevant fields
            order: [['updatedAt', 'DESC']] // Urutkan dari database
        });

        const groupedFiles = {}; // Struktur untuk mengelompokkan file: { 'YYYY': { 'MM': [files] } }

        for (const invoice of invoicesWithPdf) {
            const fullPath = invoice.pdfName; // e.g., /invoice/YYYY/MM/DD/filename.pdf
            const pathParts = fullPath.split('/').filter(p => p); // filter(p=>p) untuk menghapus string kosong

            if (pathParts.length < 4) continue; // Skip jika path tidak valid

            const [ , year, month, day ] = pathParts;

            if (!groupedFiles[year]) {
                groupedFiles[year] = {};
            }
            if (!groupedFiles[year][month]) {
                groupedFiles[year][month] = {};
            }
            if (!groupedFiles[year][month][day]) {
                groupedFiles[year][month][day] = [];
            }

            groupedFiles[year][month][day].push({
                inv_id: invoice.inv_id,
                name: `${invoice.inv_id} - ${invoice.inv_name}`,
                filename: path.basename(fullPath),
                fullPath: fullPath.startsWith('/') ? fullPath.substring(1) : fullPath, // Hapus '/' di depan
                modifiedAt: invoice.updatedAt,
            });
        }

        logger.info('[File Manager] Successfully listed and grouped PDF files from DB records.');
        return res.json({ status: true, data: groupedFiles });
    } catch (error) {
        logger.error('[File Manager] Error listing PDF files from database', { message: error.message, stack: error.stack });
        return res.status(500).json({ status: false, message: 'Failed to list PDF files.', error: error.message });
    }
};

const downloadPDFFile = async (req, res) => {
    const { filename: relativePath } = req.params; // filename is now like 'invoice/YYYY/MM/DD/INV-001.pdf'
    
    // Sanitize and validate the path to prevent path traversal
    const sanitizedPath = path.normalize(relativePath).replace(/\\/g, '/');
    if (!sanitizedPath.startsWith('invoice/') || sanitizedPath.includes('..')) {
        return res.status(400).json({ status: false, message: 'Invalid file path.' });
    }

    const absoluteFilePath = path.join(__dirname, '../../public', sanitizedPath);

    try {
        await fs.access(absoluteFilePath); // Check if file exists
        logger.info(`[File Manager] Downloading file: ${sanitizedPath}`);
        // res.download() will set the Content-Disposition header for browser download
        return res.download(absoluteFilePath, path.basename(sanitizedPath));
    } catch (error) {
        logger.error(`[File Manager] File not found for download: ${sanitizedPath}`, { message: error.message });
        return res.status(404).json({ status: false, message: 'File not found.' });
    }
};

const renamePDFFile = async (req, res) => {
    const { filename: oldRelativePath } = req.params;
    const { newFilename: newBaseFilename } = req.body;

    // Sanitize and validate old path
    const sanitizedOldPath = path.normalize(oldRelativePath).replace(/\\/g, '/');
    if (!sanitizedOldPath.startsWith('invoice/') || sanitizedOldPath.includes('..')) {
        return res.status(400).json({ status: false, message: 'Invalid old file path.' });
    }

    // Validate new filename
    if (!newBaseFilename || !newBaseFilename.toLowerCase().endsWith('.pdf') || newBaseFilename.includes('/') || newBaseFilename.includes('\\')) {
        return res.status(400).json({ status: false, message: 'New filename is required, must end with .pdf, and cannot contain path separators.' });
    }

    const oldAbsolutePath = path.join(__dirname, '../../public', sanitizedOldPath);
    const directoryOfOldFile = path.dirname(oldAbsolutePath);
    const newAbsolutePath = path.join(directoryOfOldFile, newBaseFilename);

    const oldPdfNameForDb = `/${sanitizedOldPath}`;
    const newRelativePath = path.join(path.dirname(sanitizedOldPath), newBaseFilename).replace(/\\/g, '/');
    const newPdfNameForDb = `/${newRelativePath}`;

    try {
        // 1. Rename file in filesystem
        await fs.rename(oldAbsolutePath, newAbsolutePath);
        logger.info(`[File Manager] Renamed file from ${sanitizedOldPath} to ${newRelativePath}`);

        // 2. Update file path in the database
        const [updatedRows] = await db.invoice.update(
            { pdfName: newPdfNameForDb },
            { where: { pdfName: oldPdfNameForDb } }
        );

        if (updatedRows > 0) {
            logger.info(`[File Manager] Updated database record for renamed file to ${newPdfNameForDb}.`);
        } else {
            logger.warn(`[File Manager] File was renamed, but no matching record found in database for old path ${oldPdfNameForDb}.`);
        }

        return res.json({ status: true, message: 'File renamed successfully.' });
    } catch (error) {
        logger.error(`[File Manager] Error renaming file ${sanitizedOldPath}`, { message: error.message, stack: error.stack });
        return res.status(500).json({ status: false, message: 'Failed to rename file.', error: error.message });
    }
};

const deletePDFFile = async (req, res) => {
    const { filename: relativePath } = req.params;

    // Sanitize and validate the path
    const sanitizedPath = path.normalize(relativePath).replace(/\\/g, '/');
    if (!sanitizedPath.startsWith('invoice/') || sanitizedPath.includes('..')) {
        return res.status(400).json({ status: false, message: 'Invalid file path.' });
    }

    const absoluteFilePath = path.join(__dirname, '../../public', sanitizedPath);
    const pdfNameForDb = `/${sanitizedPath}`; // Pastikan path di DB selalu mulai dengan '/'

    try {
        // 1. Delete file from filesystem
        await fs.unlink(absoluteFilePath);
        logger.info(`[File Manager] Deleted file: ${sanitizedPath}`);

        // 2. Update record in the database to mark PDF as non-existent
        const [updatedRows] = await db.invoice.update(
            { pdfCreated: false, pdfName: null },
            { where: { pdfName: pdfNameForDb } }
        );

        if (updatedRows > 0) {
            logger.info(`[File Manager] Updated database record for deleted file path ${pdfNameForDb}.`);
        } else {
            logger.warn(`[File Manager] File was deleted, but no matching record found in database for path ${pdfNameForDb}.`);
        }

        return res.json({ status: true, message: 'File deleted successfully.' });
    } catch (error) {
        if (error.code === 'ENOENT') { // Error: No such file or directory
            logger.warn(`[File Manager] Attempted to delete a non-existent file: ${sanitizedPath}`);
            // Optionally, still try to update the DB to clean up the record
            await db.invoice.update(
                { pdfCreated: false, pdfName: null },
                { where: { pdfName: pdfNameForDb } }
            );
            return res.status(404).json({ status: false, message: 'File not found.' });
        }
        logger.error(`[File Manager] Error deleting file ${sanitizedPath}`, { message: error.message, stack: error.stack });
        return res.status(500).json({ status: false, message: 'Failed to delete file.', error: error.message });
    }
};

const showChangelog = (req, res) => {
    try {
        const changelogPath = path.join(__dirname, '../CHANGELOG.md');
        
        // Baca file markdown secara sinkron (aman untuk file kecil seperti changelog)
        const markdownContent = fsSync.readFileSync(changelogPath, 'utf-8');

        // Inisialisasi parser markdown
        const md = new MarkdownIt();
        
        // Konversi markdown ke HTML
        const changelogHtml = md.render(markdownContent);

        // Render halaman EJS dan kirimkan HTML-nya
        res.render('changelog', { 
            changelogHtml,
            // Anda bisa menambahkan variabel lain yang dibutuhkan oleh layout Anda
            title: 'Riwayat Perubahan' 
        });

    } catch (error) {
        logger.error('[Changelog] Gagal membaca atau merender file changelog', { 
            message: error.message, 
            stack: error.stack 
        });
        res.status(500).send("<h1>Error 500</h1><p>Tidak dapat memuat riwayat perubahan.</p>");
    }
};

module.exports = {
    previewInvoice,
    create,
    createManualInvoice,
    getUnsendedInvoices,
    getInvoices,
    getSkippedInvoices,
    skipInvoice,
    skipInvoicesBatch,
    requeueInvoice,
    requeueInvoicesBatch,
    debugInvoiceHtml,
    getPDFFiles,
    downloadPDFFile,
    renamePDFFile,
    deletePDFFile,
    showChangelog
}