const Transfers = require('../models/TransferModel');
const Accounts = require('../models/AccountModel');
const { Op } = require('sequelize');

// CREATE TRANSFER (transfer antar rekening / top up ewallet)
const createTransfer = async (req, res) => {
    const { from_account_id, to_account_id, amount, date, description } = req.body;

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

    try {
        if (!from_account_id || !to_account_id) {
            await t.rollback();
            return res.status(400).json({ msg: "Akun sumber dan tujuan wajib diisi" });
        }

        if (from_account_id === to_account_id) {
            await t.rollback();
            return res.status(400).json({ msg: "Akun sumber dan tujuan tidak boleh sama" });
        }

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

        // Pastikan kedua akun milik user yang login
        const fromAccount = await Accounts.findOne({
            where: {
                id: from_account_id,
                user_id: req.userId
            },
            transaction: t
        });

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

        const toAccount = await Accounts.findOne({
            where: {
                id: to_account_id,
                user_id: req.userId
            },
            transaction: t
        });

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

        // Batasan: untuk saat ini tidak mendukung transfer yang melibatkan kartu kredit
        if (fromAccount.type === 'CREDIT_CARD' || toAccount.type === 'CREDIT_CARD') {
            await t.rollback();
            return res.status(400).json({
                msg: "Transfer langsung dari/ke kartu kredit belum didukung. Gunakan mekanisme pembayaran kartu kredit terpisah."
            });
        }

        // Cek saldo sumber cukup
        let fromBalance = parseFloat(fromAccount.balance || 0);
        let toBalance = parseFloat(toAccount.balance || 0);

        if (fromBalance < nominal) {
            await t.rollback();
            return res.status(400).json({ msg: "Saldo akun sumber tidak mencukupi untuk transfer" });
        }

        // Update saldo
        fromBalance -= nominal;
        toBalance += nominal;

        await fromAccount.update({ balance: fromBalance }, { transaction: t });
        await toAccount.update({ balance: toBalance }, { transaction: t });

        // Simpan record transfer
        const transfer = await Transfers.create({
            user_id: req.userId,
            from_account_id,
            to_account_id,
            amount: nominal,
            description,
            date
        }, { transaction: t });

        await t.commit();

        return res.json({
            msg: "Transfer berhasil diproses",
            data: {
                transfer,
                from_account: {
                    id: fromAccount.id,
                    name: fromAccount.name,
                    type: fromAccount.type,
                    balance: fromBalance
                },
                to_account: {
                    id: toAccount.id,
                    name: toAccount.name,
                    type: toAccount.type,
                    balance: toBalance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        return res.status(500).json({ msg: "Gagal memproses transfer saldo" });
    }
};

// GET TRANSFERS (riwayat transfer)
const getTransfers = async (req, res) => {
    const { startDate, endDate, account_id, limit } = req.query;

    try {
        const where = {
            user_id: req.userId
        };

        // Filter account_id → tampilkan transfer yang melibatkan akun ini
        if (account_id) {
            where[Op.or] = [
                { from_account_id: account_id },
                { to_account_id: account_id }
            ];
        }

        if (startDate && endDate) {
            where.date = {
                [Op.between]: [startDate, endDate]
            };
        } else if (startDate) {
            where.date = {
                [Op.gte]: startDate
            };
        }

        const transfers = await Transfers.findAll({
            where,
            include: [
                { model: Accounts, as: 'from_account', attributes: ['id', 'name', 'type'] },
                { model: Accounts, as: 'to_account', attributes: ['id', 'name', 'type'] }
            ],
            order: [
                ['date', 'DESC'],
                ['id', 'DESC']
            ],
            limit: limit ? parseInt(limit) : undefined
        });

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

// UPDATE TRANSFER
const updateTransfer = async (req, res) => {
    const { id } = req.params;
    const { from_account_id, to_account_id, amount, date, description } = req.body;

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

    try {
        // 1. Ambil transfer lama milik user login
        const existingTransfer = await Transfers.findOne({
            where: {
                id,
                user_id: req.userId
            },
            transaction: t
        });

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

        // 2. Ambil akun sumber & tujuan lama
        const oldFromAccount = await Accounts.findOne({
            where: {
                id: existingTransfer.from_account_id,
                user_id: req.userId
            },
            transaction: t
        });

        const oldToAccount = await Accounts.findOne({
            where: {
                id: existingTransfer.to_account_id,
                user_id: req.userId
            },
            transaction: t
        });

        if (!oldFromAccount || !oldToAccount) {
            await t.rollback();
            return res.status(404).json({ msg: "Akun sumber atau tujuan lama tidak ditemukan" });
        }

        const oldAmount = parseFloat(existingTransfer.amount);
        let oldFromBalance = parseFloat(oldFromAccount.balance || 0);
        let oldToBalance = parseFloat(oldToAccount.balance || 0);

        // 3. Cek apakah saldo akun tujuan masih cukup untuk mengembalikan transfer lama
        //    (kalau tidak, berarti uang sudah dipakai)
        if (oldToBalance < oldAmount) {
            await t.rollback();
            return res.status(400).json({
                msg: "Saldo akun tujuan saat ini tidak cukup untuk membatalkan transfer lama. " +
                     "Sebagian dana sudah dipakai, sehingga transfer ini tidak bisa diubah."
            });
        }

        // 4. Rollback efek transfer lama
        oldFromBalance += oldAmount; // sumber dikembalikan
        oldToBalance -= oldAmount;   // tujuan dikurangi

        await oldFromAccount.update({ balance: oldFromBalance }, { transaction: t });
        await oldToAccount.update({ balance: oldToBalance }, { transaction: t });

        // 5. Tentukan nilai baru (kalau tidak dikirim, pakai nilai lama)
        const newFromId = from_account_id || existingTransfer.from_account_id;
        const newToId = to_account_id || existingTransfer.to_account_id;
        const newAmount = amount !== undefined ? parseFloat(amount) : oldAmount;
        const newDate = date || existingTransfer.date;
        const newDescription = description !== undefined ? description : existingTransfer.description;

        if (newFromId === newToId) {
            await t.rollback();
            return res.status(400).json({ msg: "Akun sumber dan tujuan tidak boleh sama" });
        }

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

        // 6. Ambil akun sumber & tujuan baru (bisa sama atau beda dengan yang lama)
        let newFromAccount, newToAccount;

        if (newFromId === oldFromAccount.id) {
            newFromAccount = oldFromAccount;
        } else {
            newFromAccount = await Accounts.findOne({
                where: {
                    id: newFromId,
                    user_id: req.userId
                },
                transaction: t
            });
        }

        if (newToId === oldToAccount.id) {
            newToAccount = oldToAccount;
        } else {
            newToAccount = await Accounts.findOne({
                where: {
                    id: newToId,
                    user_id: req.userId
                },
                transaction: t
            });
        }

        if (!newFromAccount || !newToAccount) {
            await t.rollback();
            return res.status(404).json({ msg: "Akun sumber atau tujuan baru tidak ditemukan" });
        }

        // Batasi: tidak mendukung transfer yang melibatkan kartu kredit
        if (newFromAccount.type === 'CREDIT_CARD' || newToAccount.type === 'CREDIT_CARD') {
            await t.rollback();
            return res.status(400).json({
                msg: "Transfer dari/ke kartu kredit belum didukung."
            });
        }

        // 7. Terapkan transfer baru
        let newFromBalance = parseFloat(newFromAccount.balance || 0);
        let newToBalance = parseFloat(newToAccount.balance || 0);

        if (newFromBalance < newAmount) {
            await t.rollback();
            return res.status(400).json({
                msg: "Saldo akun sumber tidak mencukupi untuk transfer baru"
            });
        }

        newFromBalance -= newAmount;
        newToBalance += newAmount;

        await newFromAccount.update({ balance: newFromBalance }, { transaction: t });
        await newToAccount.update({ balance: newToBalance }, { transaction: t });

        // 8. Update record transfer
        await existingTransfer.update({
            from_account_id: newFromId,
            to_account_id: newToId,
            amount: newAmount,
            date: newDate,
            description: newDescription
        }, { transaction: t });

        await t.commit();

        return res.json({
            msg: "Transfer berhasil diupdate",
            data: {
                transfer: existingTransfer,
                from_account: {
                    id: newFromAccount.id,
                    name: newFromAccount.name,
                    type: newFromAccount.type,
                    balance: newFromBalance
                },
                to_account: {
                    id: newToAccount.id,
                    name: newToAccount.name,
                    type: newToAccount.type,
                    balance: newToBalance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        return res.status(500).json({ msg: "Gagal mengupdate transfer saldo" });
    }
};

// DELETE TRANSFER
const deleteTransfer = async (req, res) => {
    const { id } = req.params;

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

    try {
        // 1. Ambil transfer milik user
        const existingTransfer = await Transfers.findOne({
            where: {
                id,
                user_id: req.userId
            },
            transaction: t
        });

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

        // 2. Ambil akun sumber & tujuan
        const fromAccount = await Accounts.findOne({
            where: {
                id: existingTransfer.from_account_id,
                user_id: req.userId
            },
            transaction: t
        });

        const toAccount = await Accounts.findOne({
            where: {
                id: existingTransfer.to_account_id,
                user_id: req.userId
            },
            transaction: t
        });

        if (!fromAccount || !toAccount) {
            await t.rollback();
            return res.status(404).json({ msg: "Akun sumber atau tujuan tidak ditemukan" });
        }

        const nominal = parseFloat(existingTransfer.amount);
        let fromBalance = parseFloat(fromAccount.balance || 0);
        let toBalance = parseFloat(toAccount.balance || 0);

        // 3. Cek apakah saldo akun tujuan masih cukup untuk mengembalikan transfer
        if (toBalance < nominal) {
            await t.rollback();
            return res.status(400).json({
                msg: "Saldo akun tujuan saat ini tidak cukup untuk membatalkan transfer ini. " +
                     "Sebagian dana sudah dipakai, sehingga transfer ini tidak bisa dibatalkan."
            });
        }

        // 4. Rollback efek transfer
        fromBalance += nominal; // saldo sumber dikembalikan
        toBalance -= nominal;   // saldo tujuan dikurangi

        await fromAccount.update({ balance: fromBalance }, { transaction: t });
        await toAccount.update({ balance: toBalance }, { transaction: t });

        // 5. Hapus record transfer
        await existingTransfer.destroy({ transaction: t });

        await t.commit();

        return res.json({
            msg: "Transfer berhasil dibatalkan",
            data: {
                from_account: {
                    id: fromAccount.id,
                    name: fromAccount.name,
                    type: fromAccount.type,
                    balance: fromBalance
                },
                to_account: {
                    id: toAccount.id,
                    name: toAccount.name,
                    type: toAccount.type,
                    balance: toBalance
                }
            }
        });
    } catch (error) {
        console.log(error);
        await t.rollback();
        return res.status(500).json({ msg: "Gagal membatalkan transfer saldo" });
    }
};

module.exports = {
    createTransfer,
    getTransfers,
    updateTransfer,
    deleteTransfer
};
