const SavingGoal = require('../models/SavingGoalModel');
const SavingTransaction = require('../models/SavingTransactionModel');
const Accounts = require('../models/AccountModel');

/**
 * CREATE SAVING GOAL
 */
const createSavingGoal = async (req, res) => {
    const { name, description, target_amount, total_months, start_date, account_id } = req.body;

    try {
        const target = parseFloat(target_amount);
        const months = parseInt(total_months);

        if (isNaN(target) || target <= 0) {
            return res.status(400).json({ msg: "Target nominal tabungan tidak valid" });
        }

        if (isNaN(months) || months <= 0) {
            return res.status(400).json({ msg: "Jumlah bulan tabungan tidak valid" });
        }

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

        const goal = await SavingGoal.create({
            user_id: req.userId,
            account_id: account_id || null,
            name,
            description,
            target_amount: target,
            total_months: months,
            start_date,
            current_amount: 0,
            status: 'ONGOING'
        });

        const perMonth = target / months;

        res.json({
            msg: "Target tabungan berhasil dibuat",
            data: {
                goal,
                insight: {
                    target_amount: target,
                    total_months: months,
                    recommended_per_month: perMonth
                }
            }
        });
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal membuat target tabungan" });
    }
};

/**
 * UPDATE SAVING GOAL
 * - Kalau sudah punya transaksi tabungan, TIDAK BOLEH ubah:
 *   target_amount, total_months, start_date, account_id
 */
const updateSavingGoal = async (req, res) => {
    const { id } = req.params;
    const { name, description, target_amount, total_months, start_date, status, account_id } = req.body;

    try {
        const goal = await SavingGoal.findOne({
            where: {
                id,
                user_id: req.userId
            }
        });

        if (!goal) {
            return res.status(404).json({ msg: "Target tabungan tidak ditemukan" });
        }

        // cek apakah sudah punya transaksi tabungan
        const txCount = await SavingTransaction.count({
            where: {
                saving_goal_id: id,
                user_id: req.userId
            }
        });

        const isStructuralChangeRequested =
            target_amount !== undefined ||
            total_months !== undefined ||
            start_date !== undefined ||
            account_id !== undefined;

        if (txCount > 0 && isStructuralChangeRequested) {
            return res.status(400).json({
                msg: "Target tabungan ini sudah memiliki transaksi tabungan. " +
                     "Hapus semua transaksi tabungan terlebih dahulu sebelum mengubah target nominal, periode, tanggal mulai, atau akun utama."
            });
        }

        let newTarget = goal.target_amount;
        let newMonths = goal.total_months;

        if (target_amount !== undefined) {
            const parsedTarget = parseFloat(target_amount);
            if (isNaN(parsedTarget) || parsedTarget <= 0) {
                return res.status(400).json({ msg: "Target nominal tabungan tidak valid" });
            }
            if (parsedTarget < parseFloat(goal.current_amount)) {
                return res.status(400).json({
                    msg: "Target baru lebih kecil dari tabungan yang sudah terkumpul"
                });
            }
            newTarget = parsedTarget;
        }

        if (total_months !== undefined) {
            const parsedMonths = parseInt(total_months);
            if (isNaN(parsedMonths) || parsedMonths <= 0) {
                return res.status(400).json({ msg: "Jumlah bulan tabungan tidak valid" });
            }
            newMonths = parsedMonths;
        }

        let newStatus = goal.status;
        if (status) {
            const allowed = ['ONGOING', 'ACHIEVED', 'CANCELLED'];
            if (!allowed.includes(status)) {
                return res.status(400).json({ msg: "Status target tabungan tidak valid" });
            }
            newStatus = status;
        }

        let newAccountId = goal.account_id;
        if (account_id !== undefined) {
            if (account_id === null) {
                newAccountId = null;
            } else {
                const acc = await Accounts.findByPk(account_id);
                if (!acc) {
                    return res.status(404).json({ msg: "Akun utama tabungan tidak ditemukan" });
                }
                newAccountId = account_id;
            }
        }

        await goal.update({
            name: name !== undefined ? name : goal.name,
            description: description !== undefined ? description : goal.description,
            target_amount: newTarget,
            total_months: newMonths,
            start_date: start_date !== undefined ? start_date : goal.start_date,
            status: newStatus,
            account_id: newAccountId
        });

        res.json({ msg: "Target tabungan berhasil diupdate", data: goal });
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal mengupdate target tabungan" });
    }
};

/**
 * DELETE SAVING GOAL
 * - hanya boleh jika tidak ada transaksi tabungan
 */
const deleteSavingGoal = async (req, res) => {
    const { id } = req.params;

    try {
        const goal = await SavingGoal.findOne({
            where: {
                id,
                user_id: req.userId
            }
        });

        if (!goal) {
            return res.status(404).json({ msg: "Target tabungan tidak ditemukan" });
        }

        const txCount = await SavingTransaction.count({
            where: {
                saving_goal_id: id,
                user_id: req.userId
            }
        });

        if (txCount > 0) {
            return res.status(400).json({
                msg: "Target tabungan masih memiliki transaksi. Hapus transaksi tabungan terlebih dahulu."
            });
        }

        await goal.destroy();

        res.json({ msg: "Target tabungan berhasil dihapus" });
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal menghapus target tabungan" });
    }
};

/**
 * LIST SAVING GOALS
 */
const getSavingGoals = async (req, res) => {
    try {
        const goals = await SavingGoal.findAll({
            where: { user_id: req.userId }
        });

        const result = goals.map(g => {
            const target = parseFloat(g.target_amount);
            const current = parseFloat(g.current_amount);
            const remaining = Math.max(target - current, 0);
            const progress = target > 0 ? (current / target) * 100 : 0;

            return {
                id: g.id,
                name: g.name,
                description: g.description,
                target_amount: target,
                current_amount: current,
                remaining_amount: remaining,
                progress_percent: parseFloat(progress.toFixed(2)),
                total_months: g.total_months,
                start_date: g.start_date,
                status: g.status
            };
        });

        res.json(result);
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal mengambil data target tabungan" });
    }
};

/**
 * DETAIL SAVING GOAL + INSIGHT
 */
const getSavingGoalDetail = async (req, res) => {
    const { id } = req.params;

    try {
        const goal = await SavingGoal.findOne({
            where: {
                id,
                user_id: req.userId
            }
        });

        if (!goal) {
            return res.status(404).json({ msg: "Target tabungan tidak ditemukan" });
        }

        const target = parseFloat(goal.target_amount);
        const current = parseFloat(goal.current_amount);
        const remaining = Math.max(target - current, 0);

        const totalMonths = goal.total_months;
        const startDate = new Date(goal.start_date);
        const today = new Date();

        let monthsElapsed =
            (today.getFullYear() - startDate.getFullYear()) * 12 +
            (today.getMonth() - startDate.getMonth());

        if (monthsElapsed < 0) monthsElapsed = 0;
        if (monthsElapsed > totalMonths) monthsElapsed = totalMonths;

        let remainingMonths = totalMonths - monthsElapsed;
        if (remainingMonths <= 0) remainingMonths = 1;

        const recommendedPerMonthFromStart = target / totalMonths;
        const recommendedPerMonthFromNow =
            remaining > 0 ? remaining / remainingMonths : 0;

        const progress = target > 0 ? (current / target) * 100 : 0;

        res.json({
            id: goal.id,
            name: goal.name,
            description: goal.description,
            target_amount: target,
            current_amount: current,
            remaining_amount: remaining,
            total_months: totalMonths,
            start_date: goal.start_date,
            status: goal.status,
            progress_percent: parseFloat(progress.toFixed(2)),
            insight: {
                recommended_per_month_from_start: parseFloat(recommendedPerMonthFromStart.toFixed(2)),
                recommended_per_month_from_now: parseFloat(recommendedPerMonthFromNow.toFixed(2)),
                months_elapsed: monthsElapsed,
                months_remaining: remainingMonths
            }
        });
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal mengambil detail target tabungan" });
    }
};

/**
 * CREATE SAVING TRANSACTION (DEPOSIT / WITHDRAW)
 */
const createSavingTransaction = async (req, res) => {
    const { saving_goal_id, account_id, amount, date, type, note } = req.body;

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

    try {
        const goal = await SavingGoal.findOne({
            where: {
                id: saving_goal_id,
                user_id: req.userId
            },
            transaction: t
        });

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

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

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

        let accountBalance = parseFloat(account.balance || 0);
        let currentSaved = parseFloat(goal.current_amount || 0);

        if (type === 'DEPOSIT') {
            if (accountBalance < nominal) {
                await t.rollback();
                return res.status(400).json({ msg: "Saldo akun tidak cukup untuk menabung" });
            }
            accountBalance -= nominal;
            currentSaved += nominal;
        } else if (type === 'WITHDRAW') {
            if (currentSaved < nominal) {
                await t.rollback();
                return res.status(400).json({ msg: "Saldo tabungan tidak cukup untuk ditarik" });
            }
            accountBalance += nominal;
            currentSaved -= nominal;
        } else {
            await t.rollback();
            return res.status(400).json({ msg: "Tipe transaksi tabungan tidak valid" });
        }

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

        let newStatus = goal.status;
        const target = parseFloat(goal.target_amount);

        if (newStatus !== 'CANCELLED') {
            if (currentSaved >= target) {
                newStatus = 'ACHIEVED';
            } else {
                newStatus = 'ONGOING';
            }
        }

        await goal.update(
            { current_amount: currentSaved, status: newStatus },
            { transaction: t }
        );

        const savingTx = await SavingTransaction.create(
            {
                user_id: req.userId,
                saving_goal_id,
                account_id,
                amount: nominal,
                date,
                type,
                note
            },
            { transaction: t }
        );

        await t.commit();

        res.json({
            msg: "Transaksi tabungan berhasil diproses",
            data: {
                transaction: savingTx,
                updated_goal: {
                    current_amount: currentSaved,
                    status: newStatus
                },
                updated_account: {
                    id: account.id,
                    balance: accountBalance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        res.status(500).json({ msg: "Gagal memproses transaksi tabungan" });
    }
};

/**
 * UPDATE SAVING TRANSACTION (LIFO: hanya transaksi TERAKHIR yang boleh diubah)
 */
const updateSavingTransaction = async (req, res) => {
    const { id } = req.params;
    const { amount, date, type, account_id, note } = req.body;

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

    try {
        const existingTx = await SavingTransaction.findOne({
            where: {
                id,
                user_id: req.userId
            },
            transaction: t
        });

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

        // cek transaksi terakhir (LIFO)
        const latestTx = await SavingTransaction.findOne({
            where: {
                saving_goal_id: existingTx.saving_goal_id,
                user_id: req.userId
            },
            order: [
                ['date', 'DESC'],
                ['id', 'DESC']
            ],
            transaction: t
        });

        if (!latestTx || latestTx.id !== existingTx.id) {
            await t.rollback();
            return res.status(400).json({
                msg: "Hanya transaksi tabungan terakhir yang boleh diubah. Ubah atau hapus transaksi yang paling baru terlebih dahulu."
            });
        }

        const goal = await SavingGoal.findOne({
            where: {
                id: existingTx.saving_goal_id,
                user_id: req.userId
            },
            transaction: t
        });

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

        const oldAccount = await Accounts.findByPk(existingTx.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);
        let currentSaved = parseFloat(goal.current_amount || 0);
        const oldAmount = parseFloat(existingTx.amount);

        // rollback efek transaksi lama
        if (existingTx.type === 'DEPOSIT') {
            oldAccountBalance += oldAmount;
            currentSaved -= oldAmount;
        } else if (existingTx.type === 'WITHDRAW') {
            oldAccountBalance -= oldAmount;
            currentSaved += oldAmount;
        }

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

        const newAmount =
            amount !== undefined ? parseFloat(amount) : parseFloat(existingTx.amount);
        const newType = type || existingTx.type;
        const newAccountId = account_id || existingTx.account_id;
        const newDate = date || existingTx.date;
        const newNote = note !== undefined ? note : existingTx.note;

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

        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);
        currentSaved = parseFloat(goal.current_amount || 0);

        if (newType === 'DEPOSIT') {
            if (newAccountBalance < newAmount) {
                await t.rollback();
                return res.status(400).json({ msg: "Saldo akun tidak cukup untuk menabung" });
            }
            newAccountBalance -= newAmount;
            currentSaved += newAmount;
        } else if (newType === 'WITHDRAW') {
            if (currentSaved < newAmount) {
                await t.rollback();
                return res.status(400).json({ msg: "Saldo tabungan tidak cukup untuk ditarik" });
            }
            newAccountBalance += newAmount;
            currentSaved -= newAmount;
        } else {
            await t.rollback();
            return res.status(400).json({ msg: "Tipe transaksi tabungan tidak valid" });
        }

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

        const target = parseFloat(goal.target_amount);
        let newStatus = goal.status;

        if (newStatus !== 'CANCELLED') {
            if (currentSaved >= target) {
                newStatus = 'ACHIEVED';
            } else {
                newStatus = 'ONGOING';
            }
        }

        await goal.update(
            { current_amount: currentSaved, status: newStatus },
            { transaction: t }
        );

        await existingTx.update(
            {
                amount: newAmount,
                date: newDate,
                type: newType,
                account_id: newAccountId,
                note: newNote
            },
            { transaction: t }
        );

        await t.commit();

        res.json({
            msg: "Transaksi tabungan berhasil diupdate",
            data: {
                updated_transaction: existingTx,
                updated_goal: {
                    current_amount: currentSaved,
                    status: newStatus
                },
                updated_account: {
                    id: newAccount.id,
                    balance: newAccountBalance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        res.status(500).json({ msg: "Gagal mengupdate transaksi tabungan" });
    }
};

/**
 * DELETE SAVING TRANSACTION (LIFO: hanya transaksi TERAKHIR)
 */
const deleteSavingTransaction = async (req, res) => {
    const { id } = req.params;

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

    try {
        const existingTx = await SavingTransaction.findOne({
            where: {
                id,
                user_id: req.userId
            },
            transaction: t
        });

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

        const latestTx = await SavingTransaction.findOne({
            where: {
                saving_goal_id: existingTx.saving_goal_id,
                user_id: req.userId
            },
            order: [
                ['date', 'DESC'],
                ['id', 'DESC']
            ],
            transaction: t
        });

        if (!latestTx || latestTx.id !== existingTx.id) {
            await t.rollback();
            return res.status(400).json({
                msg: "Hanya transaksi tabungan terakhir yang boleh dihapus. Hapus transaksi yang lebih baru terlebih dahulu."
            });
        }

        const goal = await SavingGoal.findOne({
            where: {
                id: existingTx.saving_goal_id,
                user_id: req.userId
            },
            transaction: t
        });

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

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

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

        let balance = parseFloat(account.balance || 0);
        let currentSaved = parseFloat(goal.current_amount || 0);
        const nominal = parseFloat(existingTx.amount);

        if (existingTx.type === 'DEPOSIT') {
            balance += nominal;
            currentSaved -= nominal;
        } else if (existingTx.type === 'WITHDRAW') {
            balance -= nominal;
            currentSaved += nominal;
        }

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

        const target = parseFloat(goal.target_amount);
        let newStatus = goal.status;

        if (newStatus !== 'CANCELLED') {
            if (currentSaved >= target) {
                newStatus = 'ACHIEVED';
            } else {
                newStatus = 'ONGOING';
            }
        }

        await goal.update(
            { current_amount: currentSaved, status: newStatus },
            { transaction: t }
        );

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

        await t.commit();

        res.json({
            msg: "Transaksi tabungan berhasil dihapus",
            data: {
                updated_goal: {
                    current_amount: currentSaved,
                    status: newStatus
                },
                updated_account: {
                    id: account.id,
                    balance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        res.status(500).json({ msg: "Gagal menghapus transaksi tabungan" });
    }
};

/**
 * LIST TRANSAKSI TABUNGAN PER GOAL
 */
const getSavingTransactionsByGoal = async (req, res) => {
    const { id } = req.params;

    try {
        const goal = await SavingGoal.findOne({
            where: {
                id,
                user_id: req.userId
            }
        });

        if (!goal) {
            return res.status(404).json({ msg: "Target tabungan tidak ditemukan" });
        }

        const txs = await SavingTransaction.findAll({
            where: {
                saving_goal_id: id,
                user_id: req.userId
            },
            order: [['date', 'ASC']]
        });

        res.json(txs);
    } catch (error) {
        console.log(error);
        res.status(500).json({ msg: "Gagal mengambil histori transaksi tabungan" });
    }
};

module.exports = {
    createSavingGoal,
    updateSavingGoal,
    deleteSavingGoal,
    getSavingGoals,
    getSavingGoalDetail,
    createSavingTransaction,
    updateSavingTransaction,
    deleteSavingTransaction,
    getSavingTransactionsByGoal
};
