const { Wallet, WalletBalance, User, Transaction, sequelize } = require("../../models");
const { Op } = require("sequelize");
const { ValidationError, ConflictError, NotFoundError, BadRequestError } = require("../../utils/errors");
const { generateTransactionId } = require("../../utils/helpers");

class adminWalletService {
  async getWallets(query) {
    const { userId, asset, network, status, q, page = 1, limit = 20 } = query;
    const offset = (Number(page) - 1) * Number(limit);

    const where = {};
    if (userId) where.userId = userId;
    if (asset) where.asset = asset;
    if (network) where.network = network;
    if (status) where.status = status;

    if (q) {
      where[Op.or] = [
        { address: { [Op.like]: `%${q}%` } },
        { asset: { [Op.like]: `%${q}%` } },
        { network: { [Op.like]: `%${q}%` } },
      ];
    }

    const { rows, count } = await Wallet.findAndCountAll({
      where,
      include: [{ model: WalletBalance, as: "balance" }, { model: User, as: "user" }],
      order: [["created_at", "DESC"]],
      limit: Number(limit),
      offset,
    });

    return {
      count,
      page: Number(page),
      limit: Number(limit),
      wallets: rows.map(w => ({
        id: w.id,
        userId: w.userId,
        user: w.user?.name ?? null,
        asset: w.asset,
        network: w.network,
        address: w.address,
        status: w.status,
        available: Number(w.balance?.available ?? 0),
        locked: Number(w.balance?.locked ?? 0),
        createdAt: w.created_at,
      })),
    };
  }

  async getUserWallets(userId) {
    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    const wallets = await Wallet.findAll({
      where: { userId },
      include: [{ model: WalletBalance, as: "balance" }],
      order: [["created_at", "DESC"]],
    });

    return {
      user: { id: user.id, name: user.name, email: user.email },
      wallets: wallets.map(w => ({
        id: w.id,
        asset: w.asset,
        network: w.network,
        address: w.address,
        status: w.status,
        available: Number(w.balance?.available ?? 0),
        locked: Number(w.balance?.locked ?? 0),
      })),
    };
  }

  async createWallet(data) {
    const { userId, asset, network, address } = data;
    if (!userId || !asset || !network) throw new ValidationError("userId, asset, network are required");

    const user = await User.findByPk(userId);
    if (!user) throw new NotFoundError("User not found");

    const existing = await Wallet.findOne({ where: { userId, asset, network } });
    if (existing) throw new ConflictError("Wallet already exists for this asset/network");

    return await sequelize.transaction(async (t) => {
      const wallet = await Wallet.create(
        { userId, asset, network, address: address ?? null, status: "active" },
        { transaction: t }
      );

      await WalletBalance.create(
        { walletId: wallet.id, available: 0, locked: 0 },
        { transaction: t }
      );

      return wallet;
    });
  }

  async freezeWallet(walletId) {
    const wallet = await Wallet.findByPk(walletId);
    if (!wallet) throw new NotFoundError("Wallet not found");
    if (wallet.status === "frozen") throw new BadRequestError("Wallet already frozen");
    await wallet.update({ status: "frozen" });
    return wallet;
  }

  async unfreezeWallet(walletId) {
    const wallet = await Wallet.findByPk(walletId);
    if (!wallet) throw new NotFoundError("Wallet not found");
    if (wallet.status === "active") throw new BadRequestError("Wallet already active");
    await wallet.update({ status: "active" });
    return wallet;
  }

  async adjustWallet(walletId, data, adminId) {
    const { type, amount, reason } = data;
    const amt = Number(amount);

    if (!["credit", "debit"].includes(type)) throw new ValidationError("type must be credit|debit");
    if (!Number.isFinite(amt) || amt <= 0) throw new ValidationError("Invalid amount");
    if (!reason) throw new ValidationError("Reason is required");

    return await sequelize.transaction(async (t) => {
      const wallet = await Wallet.findByPk(walletId, { transaction: t, lock: t.LOCK.UPDATE });
      if (!wallet) throw new NotFoundError("Wallet not found");

      const balance = await WalletBalance.findOne({
        where: { walletId },
        transaction: t,
        lock: t.LOCK.UPDATE,
      });

      if (!balance) throw new ConflictError("Wallet balance row missing");

      const available = Number(balance.available ?? 0);
      if (type === "debit" && available < amt) throw new ValidationError("Insufficient available balance");

      const newAvailable = type === "credit" ? (available + amt) : (available - amt);

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

      const tx = await Transaction.create({
        transactionId: generateTransactionId("ADJ"),
        userId: wallet.userId,
        source: "wallet",
        kind: "adjustment",
        status: "completed",
        amount: amt,
        meta: { walletId: wallet.id, asset: wallet.asset, network: wallet.network, type, reason, adminId },
      }, { transaction: t });

      return {
        walletId: wallet.id,
        balance: { available: newAvailable, locked: Number(balance.locked ?? 0) },
        transaction: tx,
      };
    });
  }
}

module.exports = new adminWalletService();
