const Transactions = require('../models/TransactionModel');
const Accounts = require('../models/AccountModel');
const Categories = require('../models/CategoryModel');
const { Op } = require('sequelize');
const { getAccessibleUserIds } = require('../utils/userScope');


// Helper: range awal–akhir bulan dari sebuah tanggal
const getMonthRange = (dateStr) => {
    const d = dateStr ? new Date(dateStr) : new Date();
    const year = d.getFullYear();
    const month = d.getMonth();

    const firstDay = new Date(year, month, 1);
    const lastDay = new Date(year, month + 1, 0);

    const formatDate = (dt) => dt.toISOString().split('T')[0];

    return {
        start: formatDate(firstDay),
        end: formatDate(lastDay)
    };
};


// CREATE TRANSACTION
exports.createTransaction = async (req, res) => {
    const { amount, description, date, type, account_id, category_id } = req.body;

    try {
        const account = await Accounts.findByPk(account_id);
        if (!account) return res.status(404).json({ msg: "Akun tidak ditemukan" });

        const nominal = parseFloat(amount);
        if (isNaN(nominal) || nominal <= 0) {
            return res.status(400).json({ msg: "Nominal transaksi tidak valid" });
        }

        const currentBalance = parseFloat(account.balance || 0);

        // 🔹 CEK LIMIT PER KATEGORI (HANYA UNTUK EXPENSE)
        if (type === 'EXPENSE' && category_id) {
            const category = await Categories.findByPk(category_id);
            if (!category) {
                return res.status(404).json({ msg: "Kategori tidak ditemukan" });
            }

            if (category.type === 'EXPENSE') {
                const budgetLimit = parseFloat(category.budget_limit || 0);

                if (budgetLimit > 0) {
                    const { start, end } = getMonthRange(date);

                    const totalExpense = await Transactions.sum('amount', {
                        where: {
                            user_id: req.userId,
                            category_id,
                            type: 'EXPENSE',
                            date: { [Op.between]: [start, end] }
                        }
                    });

                    const spent = parseFloat(totalExpense || 0);
                    const afterThis = spent + nominal;

                    if (afterThis > budgetLimit) {
                        return res.status(400).json({
                            msg: "Pengeluaran kategori ini melebihi budget limit",
                            detail: {
                                budget_limit: budgetLimit,
                                spent,
                                remaining: budgetLimit - spent,
                                will_be_spent: afterThis
                            }
                        });
                    }
                }
            }
        }

        // 🔹 LOGIKA SALDO AKUN (BEDAKAN KARTU KREDIT vs AKUN BIASA)
        let newBalance = currentBalance;

        if (account.type === 'CREDIT_CARD') {
            const creditLimit = parseFloat(account.credit_limit || 0);
            const outstanding = currentBalance;

            if (type === 'EXPENSE') {
                if (creditLimit <= 0) {
                    return res.status(400).json({ msg: "Credit limit kartu belum diatur" });
                }
                const after = outstanding + nominal;
                if (after > creditLimit) {
                    return res.status(400).json({
                        msg: "Transaksi melebihi credit limit kartu kredit",
                        detail: {
                            credit_limit: creditLimit,
                            current_outstanding: outstanding,
                            will_be_outstanding: after
                        }
                    });
                }
                newBalance = after;
            } else if (type === 'INCOME') {
                if (outstanding < nominal) {
                    return res.status(400).json({
                        msg: "Pembayaran melebihi tagihan kartu kredit",
                        detail: {
                            current_outstanding: outstanding
                        }
                    });
                }
                newBalance = outstanding - nominal;
            } else {
                return res.status(400).json({ msg: "Tipe transaksi tidak valid" });
            }
        } else {
            // Akun biasa: CASH, BANK, E_WALLET
            if (type === 'INCOME') {
                newBalance = currentBalance + nominal;
            } else if (type === 'EXPENSE') {
                if (currentBalance < nominal) {
                    return res.status(400).json({ msg: "Saldo tidak cukup!" });
                }
                newBalance = currentBalance - nominal;
            } else {
                return res.status(400).json({ msg: "Tipe transaksi tidak valid" });
            }
        }

        await Transactions.create({
            amount: nominal,
            description,
            date,
            type,
            account_id,
            category_id,
            user_id: req.userId
        });

        await account.update({ balance: newBalance });

        res.json({ msg: "Transaksi Berhasil & Saldo Terupdate!" });
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal memproses transaksi" });
    }
};


// GET TRANSACTIONS (owner lihat miliknya + sub-account)
exports.getTransactions = async (req, res) => {
    const { startDate, endDate, category_id, limit, show_all } = req.query;

    try {
        // 🔹 pakai helper sub-account: main account melihat miliknya + sub
        const userIds = await getAccessibleUserIds(req.userId);

        const filterConditions = {
            user_id: { [Op.in]: userIds }
        };

        if (category_id) {
            filterConditions.category_id = category_id;
        }

        if (startDate && endDate) {
            filterConditions.date = { [Op.between]: [startDate, endDate] };
        } else if (startDate) {
            filterConditions.date = { [Op.gte]: startDate };
        } else if (show_all === 'true') {
            // tanpa filter tanggal
        } else {
            const date = new Date();
            const year = date.getFullYear();
            const month = date.getMonth();

            const firstDay = new Date(year, month, 1);
            const lastDay = new Date(year, month + 1, 0);

            const formatDate = (d) => d.toISOString().split('T')[0];

            filterConditions.date = {
                [Op.between]: [formatDate(firstDay), formatDate(lastDay)]
            };
        }

        const response = await Transactions.findAll({
            where: filterConditions,
            include: [
                { model: Accounts, attributes: ['name', 'type'] },
                { model: Categories, attributes: ['name', 'type'] }
                // Bisa ditambah include User kalau mau tampilkan nama pembuat transaksi
            ],
            order: [['date', 'DESC']],
            limit: limit ? parseInt(limit) : null
        });

        res.json(response);
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Error mengambil data" });
    }
};


// UPDATE TRANSACTION
exports.updateTransaction = async (req, res) => {
    const { id } = req.params;
    const { amount, description, date, type, account_id, category_id } = req.body;

    const t = await Transactions.sequelize.transaction();

    try {
        // 🔹 main-account boleh update transaksi sub-account juga
        const userIds = await getAccessibleUserIds(req.userId);

        const existingTransaction = await Transactions.findOne({
            where: {
                id,
                user_id: { [Op.in]: userIds }
            },
            transaction: t
        });

        if (!existingTransaction) {
            await t.rollback();
            return res.status(404).json({ msg: "Transaksi tidak ditemukan" });
        }

        const oldAccount = await Accounts.findByPk(existingTransaction.account_id, {
            transaction: t
        });

        if (!oldAccount) {
            await t.rollback();
            return res.status(404).json({ msg: "Akun lama tidak ditemukan" });
        }

        let oldAccountBalance = parseFloat(oldAccount.balance || 0);
        const oldAmount = parseFloat(existingTransaction.amount);

        // 🔹 ROLLBACK EFEK TRANSAKSI LAMA KE AKUN LAMA
        if (oldAccount.type === 'CREDIT_CARD') {
            // balance = outstanding
            if (existingTransaction.type === 'EXPENSE') {
                // dulu outstanding += amount → sekarang dikurangi
                oldAccountBalance -= oldAmount;
            } else if (existingTransaction.type === 'INCOME') {
                // dulu outstanding -= amount → sekarang dikembalikan
                oldAccountBalance += oldAmount;
            }
        } else {
            // akun biasa
            if (existingTransaction.type === 'INCOME') {
                oldAccountBalance -= oldAmount;
            } else if (existingTransaction.type === 'EXPENSE') {
                oldAccountBalance += oldAmount;
            }
        }

        await oldAccount.update({ balance: oldAccountBalance }, { transaction: t });

        const newAmount =
            amount !== undefined ? parseFloat(amount) : parseFloat(existingTransaction.amount);
        const newType = type || existingTransaction.type;
        const newAccountId = account_id || existingTransaction.account_id;
        const newCategoryId = category_id || existingTransaction.category_id;
        const newDate = date || existingTransaction.date;
        const newDescription = description || existingTransaction.description;

        if (isNaN(newAmount) || newAmount <= 0) {
            await t.rollback();
            return res.status(400).json({ msg: "Nominal transaksi tidak valid" });
        }

        // 🔹 CEK LIMIT PER KATEGORI UNTUK DATA BARU (EXPENSE)
        if (newType === 'EXPENSE' && newCategoryId) {
            const category = await Categories.findByPk(newCategoryId, { transaction: t });
            if (!category) {
                await t.rollback();
                return res.status(404).json({ msg: "Kategori tidak ditemukan" });
            }

            if (category.type === 'EXPENSE') {
                const budgetLimit = parseFloat(category.budget_limit || 0);

                if (budgetLimit > 0) {
                    const { start, end } = getMonthRange(newDate);

                    const totalExpense = await Transactions.sum('amount', {
                        where: {
                            user_id: existingTransaction.user_id, // pakai owner transaksi ini
                            category_id: newCategoryId,
                            type: 'EXPENSE',
                            date: { [Op.between]: [start, end] },
                            id: { [Op.ne]: existingTransaction.id }
                        },
                        transaction: t
                    });

                    const spent = parseFloat(totalExpense || 0);
                    const afterThis = spent + newAmount;

                    if (afterThis > budgetLimit) {
                        await t.rollback();
                        return res.status(400).json({
                            msg: "Pengeluaran kategori ini melebihi budget limit",
                            detail: {
                                budget_limit: budgetLimit,
                                spent,
                                remaining: budgetLimit - spent,
                                will_be_spent: afterThis
                            }
                        });
                    }
                }
            }
        }

        // 🔹 AKUN BARU / TUJUAN
        let newAccount;
        if (newAccountId === oldAccount.id) {
            newAccount = oldAccount;
        } else {
            newAccount = await Accounts.findByPk(newAccountId, { transaction: t });
            if (!newAccount) {
                await t.rollback();
                return res.status(404).json({ msg: "Akun baru tidak ditemukan" });
            }
        }

        let newAccountBalance = parseFloat(newAccount.balance || 0);

        // 🔹 TERAPKAN EFEK TRANSAKSI BARU KE AKUN BARU
        if (newAccount.type === 'CREDIT_CARD') {
            const creditLimit = parseFloat(newAccount.credit_limit || 0);
            const outstanding = newAccountBalance;

            if (newType === 'EXPENSE') {
                if (creditLimit <= 0) {
                    await t.rollback();
                    return res.status(400).json({ msg: "Credit limit kartu belum diatur" });
                }
                const after = outstanding + newAmount;
                if (after > creditLimit) {
                    await t.rollback();
                    return res.status(400).json({
                        msg: "Transaksi melebihi credit limit kartu kredit",
                        detail: {
                            credit_limit: creditLimit,
                            current_outstanding: outstanding,
                            will_be_outstanding: after
                        }
                    });
                }
                newAccountBalance = after;
            } else if (newType === 'INCOME') {
                if (outstanding < newAmount) {
                    await t.rollback();
                    return res.status(400).json({
                        msg: "Pembayaran melebihi tagihan kartu kredit",
                        detail: {
                            current_outstanding: outstanding
                        }
                    });
                }
                newAccountBalance = outstanding - newAmount;
            } else {
                await t.rollback();
                return res.status(400).json({ msg: "Tipe transaksi tidak valid" });
            }
        } else {
            // akun biasa
            if (newType === 'INCOME') {
                newAccountBalance += newAmount;
            } else if (newType === 'EXPENSE') {
                if (newAccountBalance < newAmount) {
                    await t.rollback();
                    return res.status(400).json({ msg: "Saldo tidak cukup untuk transaksi baru!" });
                }
                newAccountBalance -= newAmount;
            } else {
                await t.rollback();
                return res.status(400).json({ msg: "Tipe transaksi tidak valid" });
            }
        }

        await newAccount.update({ balance: newAccountBalance }, { transaction: t });

        await existingTransaction.update(
            {
                amount: newAmount,
                description: newDescription,
                date: newDate,
                type: newType,
                account_id: newAccountId,
                category_id: newCategoryId
            },
            { transaction: t }
        );

        await t.commit();
        return res.json({ msg: "Transaksi berhasil diupdate & saldo terupdate!" });
    } catch (error) {
        console.log(error);
        await t.rollback();
        return res.status(500).json({ msg: "Gagal mengupdate transaksi" });
    }
};


// DELETE TRANSACTION
exports.deleteTransaction = async (req, res) => {
    const { id } = req.params;
    const t = await Transactions.sequelize.transaction();

    try {
        // 🔹 main-account boleh hapus transaksi sub-account juga
        const userIds = await getAccessibleUserIds(req.userId);

        const existingTransaction = await Transactions.findOne({
            where: {
                id,
                user_id: { [Op.in]: userIds }
            },
            transaction: t
        });

        if (!existingTransaction) {
            await t.rollback();
            return res.status(404).json({ msg: "Transaksi tidak ditemukan" });
        }

        const account = await Accounts.findByPk(existingTransaction.account_id, {
            transaction: t
        });

        if (!account) {
            await t.rollback();
            return res.status(404).json({ msg: "Akun tidak ditemukan" });
        }

        let balance = parseFloat(account.balance || 0);
        const nominal = parseFloat(existingTransaction.amount);

        if (account.type === 'CREDIT_CARD') {
            // balance = outstanding
            if (existingTransaction.type === 'EXPENSE') {
                // dulu outstanding += nominal → rollback: kurangi
                balance -= nominal;
                if (balance < 0) balance = 0; // jaga-jaga
            } else if (existingTransaction.type === 'INCOME') {
                // dulu outstanding -= nominal → rollback: tambah lagi
                balance += nominal;
            }
        } else {
            // akun biasa
            if (existingTransaction.type === 'INCOME') {
                balance -= nominal;
            } else if (existingTransaction.type === 'EXPENSE') {
                balance += nominal;
            }
        }

        await account.update({ balance }, { transaction: t });

        await existingTransaction.destroy({ transaction: t });

        await t.commit();
        return res.json({ msg: "Transaksi berhasil dihapus & saldo dikembalikan!" });
    } catch (error) {
        console.log(error);
        await t.rollback();
        return res.status(500).json({ msg: "Gagal menghapus transaksi" });
    }
};


// LAPORAN TRANSAKSI MASUK (INCOME ONLY)
exports.getIncomeReport = async (req, res) => {
    const { startDate, endDate, category_id, account_id, limit } = req.query;

    try {
        // 🔹 income report juga ikut scope main + sub-account
        const userIds = await getAccessibleUserIds(req.userId);

        let whereConditions = {
            user_id: { [Op.in]: userIds },
            type: 'INCOME'
        };

        // Filter kategori (opsional)
        if (category_id) {
            whereConditions.category_id = category_id;
        }

        // Filter akun (opsional)
        if (account_id) {
            whereConditions.account_id = account_id;
        }

        // Filter tanggal
        if (startDate && endDate) {
            whereConditions.date = {
                [Op.between]: [startDate, endDate]
            };
        } else if (startDate) {
            whereConditions.date = {
                [Op.gte]: startDate
            };
        } else {
            // default: bulan berjalan, sama seperti getTransactions
            const date = new Date();
            const year = date.getFullYear();
            const month = date.getMonth();

            const firstDay = new Date(year, month, 1);
            const lastDay = new Date(year, month + 1, 0);

            const formatDate = (d) => d.toISOString().split('T')[0];

            whereConditions.date = {
                [Op.between]: [formatDate(firstDay), formatDate(lastDay)]
            };
        }

        const incomes = await Transactions.findAll({
            where: whereConditions,
            include: [
                { model: Accounts, attributes: ['id', 'name', 'type'] },
                { model: Categories, attributes: ['id', 'name', 'type'] }
            ],
            order: [
                ['date', 'DESC'],
                ['id', 'DESC']
            ],
            limit: limit ? parseInt(limit) : undefined
        });

        // Hitung total income & total per kategori
        let totalIncome = 0;
        const perCategoryMap = {};

        incomes.forEach(tx => {
            const nominal = parseFloat(tx.amount || 0);
            totalIncome += nominal;

            const cat = tx.category;
            const catId = cat ? cat.id : null;
            const catName = cat ? cat.name : 'Tanpa Kategori';

            if (!perCategoryMap[catId || 'null']) {
                perCategoryMap[catId || 'null'] = {
                    category_id: catId,
                    category_name: catName,
                    total_amount: 0
                };
            }

            perCategoryMap[catId || 'null'].total_amount += nominal;
        });

        const totalPerCategory = Object.values(perCategoryMap);

        return res.json({
            filter: {
                startDate: startDate || null,
                endDate: endDate || null,
                category_id: category_id ? Number(category_id) : null,
                account_id: account_id ? Number(account_id) : null
            },
            summary: {
                total_income: totalIncome,
                total_per_category: totalPerCategory
            },
            data: incomes
        });
    } catch (error) {
        console.log(error);
        return res.status(500).json({ msg: "Gagal mengambil laporan transaksi masuk" });
    }
};
