"use strict";

const {
  TradeHistory,
  User,
  UserBalance,
  Transaction,
  TradeSymbol,
  sequelize,
} = require("../../models");

const { Op } = require("sequelize");
const { NotFoundError, BadRequestError, ConflictError } = require("../../utils/errors");
const { generateTransactionId } = require("../../utils/helpers");

class adminTradeHistoryService {
  /**
   * List trades (global admin view)
   * Supports: q, userId, pairId, status, result, tradedBy, side, dateFrom, dateTo, page, limit
   */
  async listTrades(query = {}) {
    const {
      q,
      userId,
      pairId,
      status,
      result,
      tradedBy,
      side,
      dateFrom,
      dateTo,
      page = 1,
      limit = 20,
    } = query;

    const where = {};

    if (userId) where.userId = userId;
    if (pairId) where.pairId = pairId;
    if (status) where.status = status;
    if (result) where.result = result;
    if (tradedBy) where.tradedBy = tradedBy;
    if (side) where.side = side;

    if (dateFrom || dateTo) {
      where.createdAt = {};
      if (dateFrom) where.createdAt[Op.gte] = new Date(dateFrom);
      if (dateTo) where.createdAt[Op.lte] = new Date(dateTo);
    }

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

    const safeLimit = Math.min(Math.max(parseInt(limit, 10) || 20, 1), 100);
    const safePage = Math.max(parseInt(page, 10) || 1, 1);
    const offset = (safePage - 1) * safeLimit;

    const { rows, count } = await TradeHistory.findAndCountAll({
      where,
      order: [["created_at", "DESC"]],
      limit: safeLimit,
      offset,
    });

    return {
      count,
      page: safePage,
      limit: safeLimit,
      trades: rows,
    };
  }

  async getTradeById(tradeId) {
    const trade = await TradeHistory.findByPk(tradeId);
    if (!trade) throw new NotFoundError("Trade not found");
    return { trade };
  }

  /**
   * Create a trade manually (admin)
   * - deducts margin from user balance (volume)
   * - creates a transaction audit record
   */
  async createTrade(data, adminId) {
    const {
      userId,
      pairId,
      side,
      orderType,
      volume,
      entryPrice = null,
      requestedPrice = null,
      leverage = null,
      duration = null,
      stopLoss = null,
      takeProfit = null,
      tradedBy = "manual",
      signalId = null,
      traderId = null,
    } = data || {};

    if (!userId) throw new BadRequestError("userId is required");
    if (!pairId) throw new BadRequestError("pairId is required");

    if (!["buy", "sell"].includes(side)) throw new BadRequestError("side must be buy or sell");
    if (!["market", "limit", "stop-limit"].includes(orderType)) throw new BadRequestError("Invalid orderType");

    const vol = Number(volume);
    if (!Number.isFinite(vol) || vol <= 0) throw new BadRequestError("volume must be > 0");

    // ensure pair exists
    const pair = await TradeSymbol.findByPk(pairId);
    if (!pair) throw new NotFoundError("Trading symbol not found");

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

      const balance = await UserBalance.findOne({
        where: { userId },
        transaction: t,
        lock: t.LOCK.UPDATE,
      });
      if (!balance) throw new ConflictError("User must have a user balance");

      const currentBalance = Number(balance.balance || 0);
      if (currentBalance < vol) throw new BadRequestError("Insufficient balance");

      // deduct margin
      await balance.update({ balance: currentBalance - vol }, { transaction: t });

      // audit tx (margin debit)
      const tx = await Transaction.create(
        {
          transactionId: generateTransactionId("TRD"),
          userId,
          source: "funding",
          toType: "internal",
          kind: "trade_buy", // your enum contains trade_buy/trade_sell/etc
          direction: "debit",
          currency: user.currency,
          amount: vol,
          fee: 0,
          status: "completed",
          description: `Trade opened: ${side.toUpperCase()} ${pair.symbol} (margin ${vol})`,
          metadata: {
            adminId: adminId || null,
            trade_action: "open",
            side,
            orderType,
            pairId,
            pairSymbol: pair.symbol,
          },
        },
        { transaction: t }
      );

      const trade = await TradeHistory.create(
        {
          orderId: tx.transactionId, // keep consistent with your existing addUserTradeHistory style
          userId,
          tradedBy,
          signalId,
          traderId,
          pairId,
          side,
          orderType,
          volume: vol,
          entryPrice,
          requestedPrice,
          leverage,
          duration,
          stopLoss,
          takeProfit,
          status: "open",
          result: "none",
          profitAmount: 0,
          closedAt: null,
        },
        { transaction: t }
      );

      return { trade, transaction: tx, balance };
    });
  }

  async updateTrade(tradeId, data, adminId) {
    const trade = await TradeHistory.findByPk(tradeId);
    if (!trade) throw new NotFoundError("Trade not found");

    // You can restrict what admins can edit here if you want
    await trade.update(
      {
        ...data,
      },
      {}
    );

    return { trade };
  }

  async deleteTrade(tradeId, adminId) {
    const trade = await TradeHistory.findByPk(tradeId);
    if (!trade) throw new NotFoundError("Trade not found");

    if (String(trade.status).toLowerCase() === "open") {
      throw new BadRequestError("Cannot delete an open trade. Close it first.");
    }

    await trade.destroy();
    return { deleted: true };
  }

  /**
   * Close a trade:
   * - result: won | loss | refund
   * - profitAmount: number (required for won, optional for others)
   *
   * Balance rules:
   * - refund margin always when closing as won/refund
   * - add profitAmount when won
   * - loss => no refund by default (you can change to partial if you want)
   *
   * Creates Transaction credit when balance increases.
   */
  async closeTrade(tradeId, payload = {}, adminId) {
    const { result, profitAmount = 0, closePrice = null, notes = null } = payload;

    if (!["won", "loss", "refund"].includes(result)) {
      throw new BadRequestError("result must be one of: won, loss, refund");
    }

    const profit = Number(profitAmount || 0);
    if (!Number.isFinite(profit) || profit < 0) {
      throw new BadRequestError("profitAmount must be a valid number >= 0");
    }

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

      if (String(trade.status).toLowerCase() !== "open") {
        throw new BadRequestError("Trade is not open");
      }

      const userId = trade.userId;

      const user = await User.findByPk(userId, { transaction: t, lock: t.LOCK.UPDATE });
      if (!user) throw new NotFoundError("User not found");

      const balance = await UserBalance.findOne({
        where: { userId },
        transaction: t,
        lock: t.LOCK.UPDATE,
      });
      if (!balance) throw new ConflictError("User must have a user balance");

      const margin = Number(trade.volume || 0);

      let creditBack = 0;

      if (result === "won") {
        // refund margin + profit
        creditBack = margin + profit;
      } else if (result === "refund") {
        // refund margin only
        creditBack = margin;
      } else {
        // loss => default: nothing credited back
        creditBack = 0;
      }

      // Update trade
      await trade.update(
        {
          status: "closed",
          result,
          profitAmount: result === "won" ? profit : 0,
          closedAt: new Date(),
          // optional: store close price in requestedPrice if you don’t have a field
          // requestedPrice: closePrice ?? trade.requestedPrice,
        },
        { transaction: t }
      );

      // Update balance if needed
      if (creditBack > 0) {
        const currentBalance = Number(balance.balance || 0);
        await balance.update(
          { balance: currentBalance + creditBack },
          { transaction: t }
        );

        // Transaction credit audit
        await Transaction.create(
          {
            transactionId: generateTransactionId("PNL"),
            userId,
            source: "funding",
            toType: "internal",
            kind: "trade_profit", // your enum contains trade_profit
            direction: "credit",
            currency: user.currency,
            amount: creditBack,
            fee: 0,
            status: "completed",
            description: `Trade closed (${result}) — credit ${creditBack}`,
            metadata: {
              adminId: adminId || null,
              tradeId: trade.id,
              orderId: trade.orderId,
              result,
              margin_refund: margin,
              profit,
              closePrice,
              notes,
            },
          },
          { transaction: t }
        );
      }

      return {
        trade,
        balance,
        credited: creditBack,
      };
    });
  }
}

module.exports = new adminTradeHistoryService();