import { Router } from "express";
import { z } from "zod";
import type { RowDataPacket } from "mysql2/promise";
import { WalletTxn, WalletTopUp, User } from "@/db/models";
import { AppError, asyncHandler } from "@/lib/errors";
import { requireAuth, requireRoles } from "@/middleware/auth";
import { toPlain } from "@/lib/serialize";
import { ensureWallet, getWalletBalance, creditWallet } from "@/services/wallet";
import { getCommerceSettings } from "@/services/commerce";
import { notifyUser } from "@/services/notifications";
import { isFinancialDualWriteEnabled, withFinancialDualWrite } from "@/db/dualWriteFinancial";
import { upsertWalletTopUpMysql } from "@/db/mysql/financialWrites";
import { isAuthMysql, isWalletsMysql } from "@/db/activeDatabase";
import { createId } from "@/db/ids";
import { sqlQuery } from "@/db/mysql/pool";
import {
  listWalletTopUpsMysql,
  listWalletTxnsMysql,
  walletTopUpToApi,
  walletTxnToApi,
} from "@/db/mysql/wallets";

function walletsMysqlPrimary() {
  return isWalletsMysql() && !isFinancialDualWriteEnabled();
}

async function adminUserIds(): Promise<string[]> {
  if (isAuthMysql()) {
    const rows = await sqlQuery<RowDataPacket[]>(
      `SELECT id FROM users WHERE role = 'ADMIN' AND deleted_at IS NULL`,
    );
    return rows.map((r) => String(r.id));
  }
  const admins = await User.find({ role: "ADMIN", deletedAt: null }).select("_id").lean();
  return admins.map((a) => String(a._id));
}

export const walletRouter = Router();

const DEMO_BANKS = [
  "مصرف الجمهورية (Demo)",
  "المصرف التجاري الوطني (Demo)",
  "مصرف الصحاري (Demo)",
  "مصرف الأمان (Demo)",
];

walletRouter.get(
  "/",
  requireAuth,
  requireRoles("CUSTOMER", "ADMIN"),
  asyncHandler(async (req, res) => {
    const { wallet, balanceLyd } = await getWalletBalance(req.user!.id);
    let transactions: any[];
    if (isWalletsMysql()) {
      transactions = (await listWalletTxnsMysql({ userId: req.user!.id, take: 40 })).map(
        walletTxnToApi,
      );
    } else {
      transactions = (
        await WalletTxn.find({ userId: req.user!.id }).sort({ createdAt: -1 }).limit(40).lean()
      ).map(toPlain);
    }
    const settings = await getCommerceSettings();
    res.json({
      wallet: { id: (wallet as any)._id || (wallet as any).id, balanceLyd, currency: "LYD" },
      transactions,
      banks: DEMO_BANKS,
      settings: {
        walletEnabled: settings.walletEnabled,
      },
      _db: isWalletsMysql() ? "mysql" : "mongodb",
    });
  }),
);

walletRouter.get(
  "/topups",
  requireAuth,
  requireRoles("CUSTOMER", "ADMIN"),
  asyncHandler(async (req, res) => {
    if (isWalletsMysql()) {
      const topups = await listWalletTopUpsMysql({ userId: req.user!.id, take: 30 });
      res.json({
        topups: topups.map(walletTopUpToApi),
        _db: "mysql",
      });
      return;
    }
    const topups = await WalletTopUp.find({ userId: req.user!.id })
      .sort({ createdAt: -1 })
      .limit(30)
      .lean();
    res.json({ topups: topups.map(toPlain), _db: "mongodb" });
  }),
);

walletRouter.post(
  "/topups",
  requireAuth,
  requireRoles("CUSTOMER", "ADMIN"),
  asyncHandler(async (req, res) => {
    const settings = await getCommerceSettings();
    if (!settings.walletEnabled) throw new AppError(403, "Wallet disabled");

    const body = z
      .object({
        amountLyd: z.coerce.number().positive().max(50000),
        bankName: z.string().min(2).max(120),
        reference: z.string().min(3).max(120),
        /** Instant demo credit for testing (still logged as approved top-up) */
        instantDemo: z.boolean().optional(),
      })
      .parse(req.body);

    await ensureWallet(req.user!.id);

    if (body.instantDemo) {
      if (walletsMysqlPrimary()) {
        const id = createId();
        const now = new Date();
        await upsertWalletTopUpMysql({
          id,
          userId: req.user!.id,
          amountLyd: body.amountLyd,
          bankName: body.bankName,
          reference: body.reference,
          status: "APPROVED",
          reviewedBy: req.user!.id,
          reviewedAt: now,
          reviewNote: "Instant demo top-up",
          createdAt: now,
          updatedAt: now,
        });
        await creditWallet({
          userId: req.user!.id,
          amountLyd: body.amountLyd,
          type: "TOPUP",
          topUpId: id,
          note: "Demo bank top-up (instant)",
        });
        const { balanceLyd } = await getWalletBalance(req.user!.id);
        return res.status(201).json({
          topUp: {
            id,
            userId: req.user!.id,
            amountLyd: body.amountLyd,
            bankName: body.bankName,
            reference: body.reference,
            status: "APPROVED",
            reviewedBy: req.user!.id,
            reviewedAt: now,
            reviewNote: "Instant demo top-up",
            createdAt: now,
            updatedAt: now,
          },
          balanceLyd,
        });
      }

      const topUp = await withFinancialDualWrite({
        site: "wallet.topup.instant",
        mongoWrite: async () => {
          return await WalletTopUp.create({
            userId: req.user!.id,
            amountLyd: body.amountLyd,
            bankName: body.bankName,
            reference: body.reference,
            status: "APPROVED",
            reviewedBy: req.user!.id,
            reviewedAt: new Date(),
            reviewNote: "Instant demo top-up",
          });
        },
        mysqlWrite: async (doc) => {
          await upsertWalletTopUpMysql({
            id: String(doc._id),
            userId: req.user!.id,
            amountLyd: body.amountLyd,
            bankName: body.bankName,
            reference: body.reference,
            status: "APPROVED",
            reviewedBy: req.user!.id,
            reviewedAt: new Date(),
            reviewNote: "Instant demo top-up",
            createdAt: (doc as any).createdAt,
            updatedAt: (doc as any).updatedAt,
          });
        },
        mongoCompensate: async (doc) => {
          await WalletTopUp.deleteOne({ _id: doc._id });
        },
      });
      await creditWallet({
        userId: req.user!.id,
        amountLyd: body.amountLyd,
        type: "TOPUP",
        topUpId: String(topUp._id),
        note: "Demo bank top-up (instant)",
      });
      const { balanceLyd } = await getWalletBalance(req.user!.id);
      return res.status(201).json({ topUp: toPlain(topUp.toObject()), balanceLyd });
    }

    if (walletsMysqlPrimary()) {
      const id = createId();
      const now = new Date();
      await upsertWalletTopUpMysql({
        id,
        userId: req.user!.id,
        amountLyd: body.amountLyd,
        bankName: body.bankName,
        reference: body.reference,
        status: "PENDING",
        createdAt: now,
        updatedAt: now,
      });
      const adminIds = await adminUserIds();
      await Promise.all(
        adminIds.map((adminId) =>
          notifyUser({
            userId: adminId,
            titleAr: "طلب شحن محفظة",
            titleEn: "Wallet top-up request",
            messageAr: `طلب شحن ${body.amountLyd} د.ل بانتظار المراجعة`,
            messageEn: `Top-up of ${body.amountLyd} LYD awaiting review`,
            link: `/admin/wallets`,
          }),
        ),
      );
      return res.status(201).json({
        topUp: {
          id,
          userId: req.user!.id,
          amountLyd: body.amountLyd,
          bankName: body.bankName,
          reference: body.reference,
          status: "PENDING",
          createdAt: now,
          updatedAt: now,
        },
      });
    }

    const topUp = await withFinancialDualWrite({
      site: "wallet.topup.pending",
      mongoWrite: async () => {
        return await WalletTopUp.create({
          userId: req.user!.id,
          amountLyd: body.amountLyd,
          bankName: body.bankName,
          reference: body.reference,
          status: "PENDING",
        });
      },
      mysqlWrite: async (doc) => {
        await upsertWalletTopUpMysql({
          id: String(doc._id),
          userId: req.user!.id,
          amountLyd: body.amountLyd,
          bankName: body.bankName,
          reference: body.reference,
          status: "PENDING",
          createdAt: (doc as any).createdAt,
          updatedAt: (doc as any).updatedAt,
        });
      },
      mongoCompensate: async (doc) => {
        await WalletTopUp.deleteOne({ _id: doc._id });
      },
    });

    const adminIds = await adminUserIds();
    await Promise.all(
      adminIds.map((adminId) =>
        notifyUser({
          userId: adminId,
          titleAr: "طلب شحن محفظة",
          titleEn: "Wallet top-up request",
          messageAr: `طلب شحن ${body.amountLyd} د.ل بانتظار المراجعة`,
          messageEn: `Top-up of ${body.amountLyd} LYD awaiting review`,
          link: `/admin/wallets`,
        }),
      ),
    );

    res.status(201).json({ topUp: toPlain(topUp.toObject()) });
  }),
);
