const { Model, DataTypes, Op } = require('sequelize');
const sequelize = require('../config/db.js');
const PEntity = require('./p_entity.js');
const Entity = require('./entity.js');
const EntityHeader = require('./entity_header.js');
const BankAccount = require('./bank_account.js');
const BankBranch = require('./bank_branch.js');
const Bank = require('./bank.js');
const InvoiceTemplate = require('./invoice_template.js');
const { sanitizeSort, sanitizePagination } = require('./querySafety.js');

class Invoice extends Model {

    isMissingBankBranchIfscError(err) {
        return (
            err?.parent?.code === 'ER_BAD_FIELD_ERROR' &&
            err?.parent?.sqlMessage?.includes('bank_branch_ifsc')
        );
    }

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

    buildReadInvoiceRowQuery(id, includeEntityHeader, includeBankBranchIfsc) {
        const query = {
            where: {
                id: id
            }
        };

        if (!includeBankBranchIfsc) {
            query.attributes = Object
                .keys(Invoice.rawAttributes)
                .filter((attribute) => attribute !== 'bank_branch_ifsc');
        }

        if (includeEntityHeader) {
            query.include = [
                {
                    model: Entity,
                    as: 'entity',
                    attributes: ['id'],
                    required: false,
                    include: [
                        {
                            model: EntityHeader,
                            as: 'entity_header',
                            required: false,
                            attributes: ['id', 'ent_id', 'header_type', 'custom_text', 'logo_path']
                        }
                    ]
                }
            ];
        }

        return query;
    }

    appendEntityHeaderData(normalizedInvoice, includeEntityHeader) {
        if (!normalizedInvoice) {
            return normalizedInvoice;
        }

        const header = includeEntityHeader ? normalizedInvoice?.entity?.entity_header || null : null;

        normalizedInvoice.biller_header = header;
        normalizedInvoice.biller_header_id = header?.id || null;
        normalizedInvoice.biller_header_type = header?.header_type || null;
        normalizedInvoice.biller_header_custom_text = header?.custom_text || null;
        normalizedInvoice.biller_header_logo_path = header?.logo_path || null;
        normalizedInvoice.biller_header_logo_url = header?.id ? `/entityheader/image/${header.id}` : null;

        return normalizedInvoice;
    }

    normalizeInvoice(invoice) {
        if (!invoice) {
            return null;
        }

        const plainInvoice = typeof invoice.toJSON === 'function' ? invoice.toJSON() : { ...invoice };
        const jsonArrayFields = ['tax_id', 'billables'];
        const jsonObjectFields = ['terms', 'biller_tax_details', 'payee_tax_details'];

        jsonArrayFields.forEach((field) => {
            if (typeof plainInvoice[field] === 'string') {
                try {
                    plainInvoice[field] = JSON.parse(plainInvoice[field]);
                }
                catch (err) {
                    plainInvoice[field] = [];
                }
            }

            if (!Array.isArray(plainInvoice[field])) {
                plainInvoice[field] = [];
            }
        });

        jsonObjectFields.forEach((field) => {
            if (typeof plainInvoice[field] === 'string') {
                try {
                    plainInvoice[field] = JSON.parse(plainInvoice[field]);
                }
                catch (err) {
                    plainInvoice[field] = null;
                }
            }
        });

        if (!plainInvoice.terms || typeof plainInvoice.terms !== 'object' || Array.isArray(plainInvoice.terms)) {
            plainInvoice.terms = { delivery: [], payment: [] };
        }

        if (!Array.isArray(plainInvoice.terms.delivery)) {
            plainInvoice.terms.delivery = [];
        }

        if (!Array.isArray(plainInvoice.terms.payment)) {
            plainInvoice.terms.payment = [];
        }

        return plainInvoice;
    }

    static associate(models) {
        Invoice.belongsTo(models.Entity, {
            as: "entity",
            foreignKey: "biller_id"
        }); 
        Invoice.belongsTo(models.InvoiceTemplate, {
            as: "template",
            foreignKey: "template_id"
        });
        Invoice.hasMany(models.ChangeLog, {
            as: "change_logs",
            foreignKey: "invoice_id",   
        });
    }

    async createInvoice(data) {
        try {
            const invoice = await Invoice.create(data);
            console.log(invoice.toJSON());
            return invoice.toJSON();
        }
        catch (err) {
            console.log(err);
            return err;
        }
    }

    async getInvoices(temp_id, ent_id) {
        try {
            const invoices = await InvoiceTemplate.findOne(
                {
                    where: {
                        biller_id: ent_id,
                        id: temp_id
                    },
                    // attributes: ["id", "name", "type", "tax_id", "billables"],
                    include: [
                        {
                            model: BankAccount,
                            as: "bank_accounts",
                            attributes: ["id", "name", "account_no"],
                            include: [
                                {
                                    model: BankBranch,
                                    as: "bank_branch",
                                    attributes: ["id", "name", "ifsc"],
                                    include: [
                                        {
                                            model: Bank,
                                            as: "bank",
                                            attributes: ["id", "name"],
                                        }
                                    ]
                                }
                            ]
                        },
                        {
                            model: PEntity,
                            as: "payee",
                            attributes: ["id", "name", "pan", "gst", "tax_details", "address", "contact_no", "email"],
                        },
                        {
                            model: Entity,
                            as: "biller",
                            attributes: ["id", "name", "pan", "gst", "tax_details", "address", "contact_no", "email", "last_invoice_no", "invoice_prefix"],
                        }
                    ],
                    raw: true,
                    nest: false,
                }
            )
            console.log(invoices);
            return invoices;
        } 
        catch (err) {
            console.error("Error fetching invoices:", err);
            return [];
        }
    }    

    async getInvoiceList(id) {
        try {
            const invoices = await Invoice.findAll(
                {
                    where: {
                        biller_id: id
                    },
                    attributes: ["id", "name"]
                }
            )
            console.log(invoices);
            return invoices;
        } 
        catch (err) {
            console.log(err);
            return err;
        }
    }

    async readInvoices(id, ordCol, ordDir, offsetVal, limitVal, searchTerm) {
        try {
            const [safeOrderCol, safeOrderDir] = sanitizeSort(ordCol, ordDir, [
                'id', 'name', 'biller_name', 'payee_name', 'createdAt', 'invoice_no', 'status', 'template_id', 'biller_id', 'payee_id', 'creatorId', 'updatedAt'
            ]);
            const { safeOffset, safeLimit } = sanitizePagination(offsetVal, limitVal);
            const invoices = await Invoice.findAll(
                {
                    where: {
                        biller_id: id,
                        ...(searchTerm && {
                            name: {
                                [Op.like]: `%${searchTerm}%`
                            }
                        })
                    },
                    attributes: ["id", "name", "biller_name", "payee_name", "createdAt"],
                    order: [[safeOrderCol, safeOrderDir]],
                    offset: safeOffset,
                    limit: safeLimit
                }
            )
            console.log(invoices);
            return invoices;
        } 
        catch (err) {
            console.log(err);
            return err;
        }
    }

    async readInvoiceRow(id) {
        let includeEntityHeader = true;
        let includeBankBranchIfsc = true;

        try {
            for (let attempts = 0; attempts < 3; attempts += 1) {
                try {
                    const invoice = await Invoice.findOne(
                        this.buildReadInvoiceRowQuery(id, includeEntityHeader, includeBankBranchIfsc)
                    );

                    const normalizedInvoice = this.normalizeInvoice(invoice);
                    if (normalizedInvoice && !includeBankBranchIfsc && normalizedInvoice.bank_branch_ifsc === undefined) {
                        normalizedInvoice.bank_branch_ifsc = null;
                    }

                    const invoiceWithHeader = this.appendEntityHeaderData(normalizedInvoice, includeEntityHeader);
                    console.log(invoiceWithHeader);
                    return invoiceWithHeader;
                }
                catch (queryErr) {
                    if (includeEntityHeader && this.isMissingEntityHeadersTableError(queryErr)) {
                        includeEntityHeader = false;
                        continue;
                    }

                    if (includeBankBranchIfsc && this.isMissingBankBranchIfscError(queryErr)) {
                        includeBankBranchIfsc = false;
                        continue;
                    }

                    console.log(queryErr);
                    return queryErr;
                }
            }

            return null;
        }
        catch (err) {
            console.log(err);
            return err;
        }
    }

    async updateInvoice(id, data) {
        try {
            const invoice = await Invoice.update(data,
                {
                    where: {
                        id: id
                    }
                }
            )
            console.log(invoice);
            return invoice;
        }
        catch (err) {
            console.log(err);
            return err;
        }
    }

    async countInvoices(id) {
        try {
            const count = await Invoice.count(
                {
                    where: {
                        biller_id: id
                    }
                }
            )
            console.log(count);
            return count;
        } 
        catch (err) {
            console.log(err);
            return err;
        }
    }
}

Invoice.init(
    {
        id: {
            type: DataTypes.INTEGER,
            primaryKey: true,
            autoIncrement: true
        },
        template_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        creatorId: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        user_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        type: {
            type: DataTypes.STRING,
            allowNull: false
        },
        status: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        recurrence: {
            type: DataTypes.STRING,
            allowNull: false
        },
        note: {
            type: DataTypes.TEXT,
            allowNull: true
        },
        biller_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        biller_name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        biller_email: {
            type: DataTypes.STRING,
            allowNull: true
        },
        biller_contact_no: {
            type: DataTypes.STRING,
            allowNull: true
        },
        biller_address: {
            type: DataTypes.STRING,
            allowNull: true
        },
        biller_gst: {
            type: DataTypes.STRING,
            allowNull: true
        },
        biller_pan: {
            type: DataTypes.STRING,
            allowNull: true
        },
        biller_tax_details: {
            type: DataTypes.JSON,
            allowNull: true
        },
        payee_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        payee_name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        payee_email: {
            type: DataTypes.STRING,
            allowNull: true
        },
        payee_contact_no: {
            type: DataTypes.STRING,
            allowNull: true
        },
        payee_address: {
            type: DataTypes.STRING,
            allowNull: true
        },
        payee_gst: {
            type: DataTypes.STRING,
            allowNull: true
        },
        payee_pan: {
            type: DataTypes.STRING,
            allowNull: true
        },
        payee_tax_details: {
            type: DataTypes.JSON,
            allowNull: true
        },
        acc_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        bank_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        bank_name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        bank_branch_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        bank_branch_name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        bank_branch_ifsc: {
            type: DataTypes.STRING,
            allowNull: true
        },
        bank_account_id: {
            type: DataTypes.INTEGER,
            allowNull: false
        },
        bank_account_name: {
            type: DataTypes.STRING,
            allowNull: false
        },
        bank_account_no: {
            type: DataTypes.STRING,
            allowNull: false
        },
        tax_id: {
            type: DataTypes.JSON,
            allowNull: true
        },
        billables: {
            type: DataTypes.JSON,
            allowNull: true
        },
        terms: {
            type: DataTypes.JSON,
            allowNull: true
        },
        invoice_no: {
            type: DataTypes.STRING(25),
            allowNull: true
        },
        change_id: {
            type: DataTypes.INTEGER,
            allowNull: true,
            references: {
                model: 'change_logs',
                key: 'id'
            }
        }
    },
    {
        sequelize,
        modelName: "Invoice",
        tableName: "invoices",
    }
)

module.exports = Invoice;