"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPaymentTransactionsController = createPaymentTransactionsController;
const menu_js_1 = require("../../app/menu.js");
const PaymentTransactionModel_js_1 = require("../../infrastructure/database/models/PaymentTransactionModel.js");
const PaymentSweeper_js_1 = require("../../infrastructure/payments/PaymentSweeper.js");
const BalanceService_js_1 = require("../../infrastructure/payments/BalanceService.js");
const SubscriptionService_js_1 = require("../../application/subscriptions/SubscriptionService.js");
const AdminPinService_js_1 = require("../../infrastructure/auth/AdminPinService.js");
const AdminEvents_js_1 = require("../../infrastructure/events/AdminEvents.js");
const Amount_js_1 = require("../../infrastructure/payments/Amount.js");
const types_js_1 = require("../../domain/subscriptions/types.js");
const Logger_js_1 = require("../../shared/logger/Logger.js");
function createPaymentTransactionsController(config, cryptoBox, bot) {
    const logger = new Logger_js_1.Logger(config.logLevel);
    const balanceService = new BalanceService_js_1.BalanceService(config, logger);
    const subscriptionService = new SubscriptionService_js_1.SubscriptionService(config, undefined, balanceService, logger);
    const emitPaymentConfirmedEvent = async (id) => {
        const tx = await PaymentTransactionModel_js_1.PaymentTransactionModel.findById(id).lean();
        if (!tx)
            return;
        AdminEvents_js_1.adminEvents.emitPaymentConfirmed({
            id: tx._id.toString(),
            telegramId: tx.telegramId,
            username: tx.username,
            selectedPlan: tx.selectedPlan,
            network: tx.network,
            walletAddress: tx.walletAddress,
            expectedAmount: tx.expectedAmount,
            paidAmount: tx.paidAmount,
            status: tx.status,
            createdAt: tx.createdAt.toISOString()
        });
    };
    return {
        list: async (req, res) => {
            const { status, unswept } = req.query;
            const filter = {};
            if (status) {
                filter.status = status;
            }
            if (unswept === "true") {
                filter.status = "confirmed";
                filter.sweptAt = { $exists: false };
            }
            const transactions = await PaymentTransactionModel_js_1.PaymentTransactionModel.find(filter).sort({ createdAt: -1 }).lean();
            res.json({
                data: transactions.map((tx) => ({
                    id: tx._id.toString(),
                    telegramId: tx.telegramId,
                    username: tx.username,
                    selectedPlan: tx.selectedPlan,
                    network: tx.network,
                    walletAddress: tx.walletAddress,
                    expectedAmount: tx.expectedAmount,
                    paidAmount: tx.paidAmount,
                    status: tx.status,
                    confirmedAt: tx.confirmedAt?.toISOString(),
                    manuallyConfirmedAt: tx.manuallyConfirmedAt?.toISOString(),
                    manuallyConfirmedBy: tx.manuallyConfirmedBy,
                    sweptAt: tx.sweptAt?.toISOString(),
                    sweptAmount: tx.sweptAmount,
                    sweepTxHash: tx.sweepTxHash,
                    sweepFailureReason: tx.sweepFailureReason,
                    createdAt: tx.createdAt.toISOString()
                }))
            });
        },
        confirm: async (req, res) => {
            const id = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
            const { adminKey, confirmedBy } = req.body;
            const isValidPin = adminKey ? await AdminPinService_js_1.AdminPinService.verifyPin(adminKey) : false;
            if (!isValidPin) {
                res.status(403).json({ error: "Invalid admin PIN." });
                return;
            }
            const transaction = await PaymentTransactionModel_js_1.PaymentTransactionModel.findById(id).lean();
            if (!transaction) {
                res.status(404).json({ error: "Transaction not found." });
                return;
            }
            if (transaction.status !== "pending") {
                res.status(409).json({ error: `Transaction is already ${transaction.status}.` });
                return;
            }
            try {
                const currentBalance = await balanceService.getUsdtBalance(transaction.network, transaction.walletAddress);
                const paidDiff = BigInt(currentBalance) > BigInt(transaction.startingBalance)
                    ? BigInt(currentBalance) - BigInt(transaction.startingBalance)
                    : 0n;
                const paidAmount = paidDiff > 0n ? paidDiff.toString() : transaction.expectedAmount;
                await subscriptionService.activateSubscription(transaction, paidAmount);
                await PaymentTransactionModel_js_1.PaymentTransactionModel.updateOne({ _id: transaction._id }, {
                    $set: {
                        manuallyConfirmedAt: new Date(),
                        manuallyConfirmedBy: confirmedBy || "admin"
                    }
                });
                await emitPaymentConfirmedEvent(id);
                if (bot) {
                    try {
                        const decimals = await balanceService.getDecimals(transaction.network);
                        await bot.telegram.sendMessage(transaction.telegramId, subscriptionActivatedMessage((0, Amount_js_1.formatTokenUnits)(BigInt(paidAmount), decimals), types_js_1.NETWORK_LABELS[transaction.network].name), (0, menu_js_1.platformsKeyboard)());
                    }
                    catch (notifyError) {
                        logger.error("Failed to send Telegram confirmation after manual confirmation.", {
                            transactionId: id,
                            error: notifyError instanceof Error ? notifyError.message : String(notifyError)
                        });
                    }
                }
                const updated = await PaymentTransactionModel_js_1.PaymentTransactionModel.findById(id).lean();
                if (!updated) {
                    res.status(500).json({ error: "Failed to retrieve updated transaction." });
                    return;
                }
                res.json({
                    data: {
                        id: updated._id.toString(),
                        telegramId: updated.telegramId,
                        username: updated.username,
                        selectedPlan: updated.selectedPlan,
                        network: updated.network,
                        walletAddress: updated.walletAddress,
                        expectedAmount: updated.expectedAmount,
                        paidAmount: updated.paidAmount,
                        status: updated.status,
                        confirmedAt: updated.confirmedAt?.toISOString(),
                        manuallyConfirmedAt: updated.manuallyConfirmedAt?.toISOString(),
                        manuallyConfirmedBy: updated.manuallyConfirmedBy,
                        createdAt: updated.createdAt.toISOString()
                    }
                });
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                logger.error("Manual payment confirmation failed.", { transactionId: id, error: message });
                res.status(500).json({ error: message });
            }
        },
        delete: async (req, res) => {
            const id = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
            const { adminKey } = req.body;
            const isValidPin = adminKey ? await AdminPinService_js_1.AdminPinService.verifyPin(adminKey) : false;
            if (!isValidPin) {
                res.status(403).json({ error: "Invalid admin PIN." });
                return;
            }
            const transaction = await PaymentTransactionModel_js_1.PaymentTransactionModel.findById(id).lean();
            if (!transaction) {
                res.status(404).json({ error: "Transaction not found." });
                return;
            }
            if (transaction.status !== "confirmed" || !transaction.manuallyConfirmedAt) {
                res.status(409).json({ error: "Only manually confirmed transactions can be deleted." });
                return;
            }
            await PaymentTransactionModel_js_1.PaymentTransactionModel.deleteOne({ _id: transaction._id });
            logger.info("Manually confirmed payment transaction deleted.", {
                transactionId: id,
                telegramId: transaction.telegramId
            });
            res.json({ success: true });
        },
        decryptedPrivateKey: async (req, res) => {
            const id = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
            const { adminKey } = req.body;
            const isValidPin = adminKey ? await AdminPinService_js_1.AdminPinService.verifyPin(adminKey) : false;
            if (!isValidPin) {
                res.status(403).json({ error: "Invalid admin PIN." });
                return;
            }
            const transaction = await PaymentTransactionModel_js_1.PaymentTransactionModel.findById(id).lean();
            if (!transaction) {
                res.status(404).json({ error: "Transaction not found." });
                return;
            }
            if (!transaction.encryptedPrivateKey) {
                res.status(500).json({ error: "Encrypted private key is missing." });
                return;
            }
            try {
                const privateKey = cryptoBox.decrypt(transaction.encryptedPrivateKey);
                res.json({
                    data: {
                        id: transaction._id.toString(),
                        telegramId: transaction.telegramId,
                        username: transaction.username,
                        network: transaction.network,
                        walletAddress: transaction.walletAddress,
                        privateKey: privateKey
                    }
                });
            }
            catch (error) {
                const message = error instanceof Error ? error.message : String(error);
                logger.error("Failed to decrypt payment transaction private key.", {
                    transactionId: id,
                    error: message
                });
                res.status(500).json({ error: message });
            }
        },
        sweep: async (req, res) => {
            const { vaultId } = req.body;
            const sweeper = new PaymentSweeper_js_1.PaymentSweeper(config, cryptoBox, logger);
            const summary = await sweeper.sweepUnsweptTransactions(vaultId);
            res.json(summary);
        },
        sweepOne: async (req, res) => {
            const id = Array.isArray(req.params.id) ? req.params.id[0] : req.params.id;
            const { vaultId } = req.body;
            const sweeper = new PaymentSweeper_js_1.PaymentSweeper(config, cryptoBox, logger);
            const summary = await sweeper.sweepTransactionIds([id], vaultId);
            res.json(summary);
        },
        sweepSelected: async (req, res) => {
            const { ids, vaultId } = req.body;
            if (!Array.isArray(ids) || ids.length === 0) {
                res.status(400).json({ error: "Missing or invalid 'ids' array." });
                return;
            }
            const sweeper = new PaymentSweeper_js_1.PaymentSweeper(config, cryptoBox, logger);
            const summary = await sweeper.sweepTransactionIds(ids, vaultId);
            res.json(summary);
        }
    };
}
const subscriptionActivatedMessage = (paidAmount, networkName) => [
    `Payment confirmed: ${paidAmount} USDT on ${networkName}.`,
    "Your premium subscription is now active.",
    "",
    "Quant Whisper surfaces live DeFi trading signals from on-chain and market data.",
    "",
    "How to use the bot:",
    "1. Select a trading platform below.",
    "2. Choose a signal category and review the setup, risk notes, and market context.",
    "3. Use /payment_status or My Subscription to check your access anytime.",
    "",
    "Signals are educational and are not financial advice."
].join("\n");
