const Deposit = require('../models/deposit.model');
const CardType = require('../models/cardType.model');
const User = require('../models/users.model');
const Transaction = require('../models/transaction.model');
const { BadRequestError } = require('../core/error.response');
const { emitToUser } = require('../socket');
const SystemConfigService = require('./systemConfig.service');
require('dotenv').config();

const axios = require('axios');

const { SePayPgClient } = require('sepay-pg-node');
const uploadCardTSR = require('../utils/uploadCardTSR');

class DepositService {
    generateCode() {
        const random = Math.random().toString(36).substring(2, 7); // 5 ký tự
        const time = Date.now().toString().slice(-3); // 3 số cuối ms

        return `${random.toUpperCase()}${time}`;
    }

    // Get all active card types with merged fees
    async getCardTypes() {
        const cfg = await SystemConfigService.getConfig();
        const TSR_URL = cfg.tsrUrl || process.env.URL_TSR;
        const TSR_PARTNER_ID = cfg.tsrPartnerId || process.env.PARTNER_ID_TST;

        const [dataCardType, localConfigs] = await Promise.all([
            axios.get(`${TSR_URL}/getfee?partner_id=${TSR_PARTNER_ID}`),
            CardType.find(),
        ]);

        const providerFees = dataCardType.data;

        // Merge provider fees with local config
        const mergedList = providerFees
            .map((item) => {
                const config = localConfigs.find((c) => c.telco === item.telco && c.value === item.value);

                // If local config exists, use it. Otherwise use provider fee.
                // If local config says status=false, we might want to hide it,
                // but for Admin we show all. For User we filter.
                // Let's return everything and let Controller/Frontend decide based on role?
                // Or typically getCardTypes is public, so we filter hidden ones.

                if (config) {
                    if (!config.status) return null; // Hidden by admin
                    return {
                        ...item,
                        fees: config.fee, // Override fee with local config
                        _id: config._id, // Include ID for admin reference
                    };
                }

                // If no local config, use provider default
                return item;
            })
            .filter((item) => item !== null);

        return mergedList;
    }

    // Admin: Get raw provider list + local configs for management
    async getAllCardConfigs() {
        const cfg = await SystemConfigService.getConfig();
        const TSR_URL = cfg.tsrUrl || process.env.URL_TSR;
        const TSR_PARTNER_ID = cfg.tsrPartnerId || process.env.PARTNER_ID_TST;

        const [dataCardType, localConfigs] = await Promise.all([
            axios.get(`${TSR_URL}/getfee?partner_id=${TSR_PARTNER_ID}`),
            CardType.find(),
        ]);

        const providerFees = dataCardType.data;

        return providerFees.map((item) => {
            const config = localConfigs.find((c) => c.telco === item.telco && c.value === item.value);
            return {
                ...item,
                customFee: config ? config.fee : null,
                customStatus: config ? config.status : true,
                configId: config ? config._id : null,
            };
        });
    }

    // Admin: Update or Create card config
    async updateCardConfig(data) {
        const { telco, value, fee, status } = data;

        // Upsert: Find one and update, or create new
        const result = await CardType.findOneAndUpdate(
            { telco, value },
            { telco, value, fee, status },
            { new: true, upsert: true },
        );
        return result;
    }

    async createQRBank(userId, amount) {
        const user = await User.findById(userId);
        if (!user) {
            throw new BadRequestError('Không tìm thấy người dùng');
        }

        const cfg = await SystemConfigService.getConfig();
        const websiteService = require('./website.service');
        const websiteData = await websiteService.getWebsite();
        const domain = (websiteData?.domain || process.env.URL_CLIENT || 'http://localhost:5173').replace(/\/$/, '');
        console.log(cfg);

        const client = new SePayPgClient({
            env: cfg.sepayMode || 'sandbox',
            merchant_id: cfg.sepayId || process.env.SEPAY_ID_SANBOX,
            secret_key: cfg.sepaySecret || process.env.SEPAY_SECRET_SANBOX,
        });

        const checkoutURL = client.checkout.initCheckoutUrl();

        const checkoutFormfields = client.checkout.initOneTimePaymentFields({
            operation: 'PURCHASE',
            payment_method: 'BANK_TRANSFER',
            order_invoice_number: this.generateCode(),
            order_amount: amount,
            currency: 'VND',
            order_description: `SEVQR ${userId}`,
            success_url: `${domain}/deposit/bank?status=success`,
            error_url: `${domain}/deposit/bank?status=error`,
            cancel_url: `${domain}/deposit/bank`,
        });

        return { checkoutURL, checkoutFormfields };
    }

    async checkBankQR(order_description, order_amount) {
        const userId = order_description.split(' ')[1];
        const user = await User.findById(userId);
        if (!user) {
            throw new BadRequestError('Không tìm thấy người dùng');
        }

        const newDeposit = new Deposit({
            userId,
            type: 'BANK',
            network: 'BANK_TRANSFER',
            code: order_description,
            serial: order_description,
            amount: order_amount,
            receivedAmount: order_amount,
            status: 'success',
        });

        const balanceBefore = user.balance;
        user.balance += order_amount;
        user.balanceHistory += order_amount;
        await user.save();

        const savedDeposit = await newDeposit.save();

        // Create transaction log
        await Transaction.create({
            userId,
            transCode: `ATM-${this.generateCode()}`,
            type: 'DEPOSIT',
            amount: order_amount,
            balanceBefore,
            balanceAfter: user.balance,
            content: `Nạp tiền qua ngân hàng (Auto): ${order_amount.toLocaleString()}đ`,
        });

        return savedDeposit;
    }

    // Admin: Update card type
    async updateCardType(id, data) {
        const { name, fee, status } = data;
        const updated = await CardType.findByIdAndUpdate(id, { name, fee, status }, { new: true });
        if (!updated) {
            throw new BadRequestError('Không tìm thấy loại thẻ');
        }
        return updated;
    }

    // Admin: Delete card type
    async deleteCardType(id) {
        const deleted = await CardType.findByIdAndDelete(id);
        if (!deleted) {
            throw new BadRequestError('Không tìm thấy loại thẻ');
        }
        return deleted;
    }

    // User: Create deposit request
    async createDeposit(userId, data) {
        const { type, network, code, serial, amount } = data;

        if (type !== 'CARD') {
            throw new BadRequestError('Chưa hỗ trợ phương thức này');
        }

        // Fetch merged configs to get the correct fee
        const validCards = await this.getCardTypes();
        const selectedCard = validCards.find((c) => c.telco === network && c.value === amount);

        if (!selectedCard) {
            throw new BadRequestError('Loại thẻ hoặc mệnh giá không hợp lệ (hoặc đang bảo trì)');
        }

        const receivedAmount = amount - (amount * selectedCard.fees) / 100;

        const newDeposit = new Deposit({
            userId,
            type,
            network,
            code,
            serial,
            amount,
            receivedAmount,
            status: 'pending',
            date: new Date(),
        });

        await newDeposit.save();

        try {
            const dataTest = await uploadCardTSR(newDeposit._id, network, code, serial, amount, 'charging');

            // Handle response from TSR
            if (dataTest.status === 1) {
                // Success
                newDeposit.status = 'success';
                await newDeposit.save();

                const user = await User.findById(userId);
                if (user) {
                    const balanceBefore = user.balance;
                    user.balance += receivedAmount;
                    user.balanceHistory += receivedAmount;
                    await user.save();

                    await Transaction.create({
                        userId: user._id,
                        transCode: `CARD-${this.generateCode()}`,
                        type: 'DEPOSIT',
                        amount: receivedAmount,
                        balanceBefore,
                        balanceAfter: user.balance,
                        content: `Nạp thẻ ${network} - Mệnh giá ${amount.toLocaleString()}đ`,
                    });
                }

                return {
                    success: true,
                    message: 'Nạp thẻ thành công',
                    data: newDeposit,
                };
            } else if (dataTest.status === 2) {
                // Wrong amount (Success but penalized?)
                // Usually status 2 means "Thẻ sai mệnh giá" -> provider usually charges penalty or rejects
                // For now, let's treat as pending/warning or fail depending on business rule.
                // User said "Thẻ đúng nhưng sai mệnh giá"
                newDeposit.status = 'refused'; // or some other status
                newDeposit.note = 'Sai mệnh giá';
                await newDeposit.save();

                return {
                    success: true,
                    message: 'Thẻ đúng nhưng sai mệnh giá. Vui lòng chờ Admin kiểm tra.',
                    data: newDeposit,
                };
            } else if (dataTest.status === 99) {
                // Pending
                return {
                    success: true, // It's "success" in terms of submission
                    message: 'Thẻ đang chờ xử lý',
                    data: newDeposit,
                };
            } else {
                // Failed (3, 4, 100, etc)
                newDeposit.status = 'reject'; // Using 'reject' or 'failed' based on schema enum
                newDeposit.note = dataTest.message;
                await newDeposit.save();

                throw new BadRequestError(dataTest.message || 'Nạp thẻ thất bại');
            }
        } catch (error) {
            // If local logic error or network error, ensure we update deposit if needed
            // But if we already threw BadRequestError, it goes up.
            throw error;
        }
    }

    // Admin: Update deposit status
    async updateDepositStatus(id, status) {
        const deposit = await Deposit.findById(id);
        if (!deposit) {
            throw new BadRequestError('Không tìm thấy yêu cầu nạp tiền');
        }

        if (deposit.status !== 'pending') {
            throw new BadRequestError('Yêu cầu này đã được xử lý');
        }

        deposit.status = status;
        await deposit.save();

        if (status === 'success') {
            const user = await User.findById(deposit.userId);
            if (user) {
                const balanceBefore = user.balance;
                user.balance += deposit.receivedAmount;
                user.balanceHistory += deposit.receivedAmount;
                await user.save();

                // Create transaction log
                await Transaction.create({
                    userId: user._id,
                    transCode: `CARD-${this.generateCode()}`,
                    type: 'DEPOSIT',
                    amount: deposit.receivedAmount,
                    balanceBefore,
                    balanceAfter: user.balance,
                    content: `Nạp thẻ ${deposit.network} - Mệnh giá ${deposit.amount.toLocaleString()}đ`,
                });

                // Emit real-time notification to the user
                emitToUser(String(user._id), 'balance_updated', {
                    newBalance: user.balance,
                    amount: deposit.receivedAmount,
                    message: `Tài khoản của bạn vừa được cộng ${deposit.receivedAmount.toLocaleString()}đ`,
                });
            }
        }

        return deposit;
    }

    // Admin: Get all deposits
    async getAllDeposits() {
        return await Deposit.find().populate('userId', 'fullName email').sort({ createdAt: -1 });
    }

    // User: Get personal deposit history
    async getUserDeposits(userId) {
        return await Deposit.find({ userId }).sort({ createdAt: -1 });
    }

    // User: Get personal transaction history
    async getUserTransactions(userId) {
        return await Transaction.find({ userId }).sort({ createdAt: -1 });
    }

    // User: Get stats for dashboard
    async getUserStats(userId) {
        const user = await User.findById(userId);
        if (!user) throw new BadRequestError('Không tìm thấy người dùng');

        const deposits = await Deposit.find({ userId, status: 'success' });
        const totalDeposited = deposits.reduce((sum, d) => sum + d.receivedAmount, 0);

        // Sum deposits this month
        const startOfMonth = new Date();
        startOfMonth.setDate(1);
        startOfMonth.setHours(0, 0, 0, 0);

        const depositsThisMonth = deposits.filter((d) => new Date(d.createdAt) >= startOfMonth);
        const totalDepositedMonth = depositsThisMonth.reduce((sum, d) => sum + d.receivedAmount, 0);

        // Total spent = (Total Deposited) - (Current Balance)?
        // Not quite accurate if there are refunds or initial balances,
        // but for now let's calculate based on PURCHASE type transactions
        const purchaseTransactions = await Transaction.find({ userId, type: 'PURCHASE' });
        const totalSpent = purchaseTransactions.reduce((sum, t) => sum + Math.abs(t.amount), 0);

        return {
            balance: user.balance,
            totalDeposited,
            totalSpent,
            totalDepositedMonth,
        };
    }

    async checkTSRService(status, request_id) {
        if (status === '1') {
            const deposit = await Deposit.findOne({ _id: request_id });
            if (deposit) {
                deposit.status = 'success';
                await deposit.save();
                const findDeposit = await Deposit.findById(deposit._id);
                const user = await User.findById(findDeposit.userId);
                if (user) {
                    const balanceBefore = user.balance;
                    user.balance += findDeposit.receivedAmount;
                    user.balanceHistory += findDeposit.receivedAmount;
                    await user.save();
                    await Transaction.create({
                        userId: user._id,
                        transCode: `CARD-${this.generateCode()}`,
                        type: 'DEPOSIT',
                        amount: findDeposit.receivedAmount,
                        balanceBefore,
                        balanceAfter: user.balance,
                        content: `Nạp thẻ ${findDeposit.network} - Mệnh giá ${findDeposit.amount.toLocaleString()}đ`,
                    });
                }
            } else if (status === '1') {
                await Deposit.updateOne({ _id: request_id }, { status: 'failed' });
                throw new BadRequestError('Yêu cầu nạp tiền không hợp lệ');
            }
        }
    }
}

module.exports = new DepositService();
