const { Op } = require('sequelize');

class CentralService {

async tradeWatcher() {
    const now = Date.now();

    // 1. Binary trades (auto close)
    const binaries = await BinaryTrade.findAll({
        where: {
            status: "open",
            closeTime: { [Op.lte]: now }
        },
        limit: 500
    });

    // 2. Spot orders (TP/SL)
    const spots = await SpotOrder.findAll({
        where: {
            status: "open"
        },
        limit: 500
    });

    // 3. Derivatives positions
    const positions = await DerivativePosition.findAll({
        where: {
            status: "open"
        },
        limit: 500
    });

    await Promise.all([
        ...binaries.map(t => processBinary(t)),
        ...spots.map(o => processSpot(o)),
        ...positions.map(p => processDerivative(p))
    ]);
}

async  processBinary(trade) {
  await settleBinaryTrade(trade.id);
}

async  settleBinaryTrade(tradeId) {
  const tx = await sequelize.transaction();

  try {
    const trade = await BinaryTrade.findByPk(tradeId, {
      transaction: tx,
      lock: tx.LOCK.UPDATE
    });

    if (!trade || trade.status !== "open") {
      await tx.rollback();
      return;
    }

    const price = await getPrice(trade.assetId);

    const result = resolveTradeStatus(
      trade.direction,
      trade.entryPrice,
      price
    );

    let payout = 0;

    if (result === "won") {
      payout = trade.amount * trade.payoutRatio;

      await applyLedgerEntry({
        userId: trade.userId,
        amount: payout,
        type: "credit",
        referenceId: trade.id,
        referenceType: "binary",
        description: "Binary win",
        transaction: tx
      });
    }

    if (result === "draw") {
      payout = trade.amount;

      await applyLedgerEntry({
        userId: trade.userId,
        amount: payout,
        type: "credit",
        referenceType: "binary",
        referenceId: trade.id,
        description: "Refund",
        transaction: tx
      });
    }

    await trade.update({
      status: result,
      resultPrice: price,
      payoutAmount: payout,
      settledAt: new Date()
    }, { transaction: tx });

    await tx.commit();

  } catch (err) {
    await tx.rollback();
  }
}

async  processSpot(order) {
  const price = await getPrice(order.assetId);

  let shouldClose = false;

  if (order.takeProfit && price >= order.takeProfit) {
    shouldClose = true;
  }

  if (order.stopLoss && price <= order.stopLoss) {
    shouldClose = true;
  }

  if (!shouldClose) return;

  const tx = await sequelize.transaction();

  try {
    const o = await SpotOrder.findByPk(order.id, {
      transaction: tx,
      lock: tx.LOCK.UPDATE
    });

    if (!o || o.status !== "open") {
      await tx.rollback();
      return;
    }

    const pnl = (price - o.entryPrice) * o.quantity;

    await applyLedgerEntry({
      userId: o.userId,
      amount: o.margin + pnl,
      type: "credit",
      referenceId: o.id,
      referenceType: "spot",
      description: "Spot close",
      transaction: tx
    });

    await o.update({
      status: "closed",
      exitPrice: price,
      profit: pnl
    }, { transaction: tx });

    await tx.commit();

  } catch (err) {
    await tx.rollback();
  }
}

async  processDerivative(position) {
  const price = await getPrice(position.assetId);

  let closeReason = null;

  // TP
  if (position.takeProfit && price >= position.takeProfit) {
    closeReason = "tp";
  }

  // SL
  if (position.stopLoss && price <= position.stopLoss) {
    closeReason = "sl";
  }

  // Liquidation
  const liquidationPrice = calculateLiquidationPrice(position);

  if (
    (position.side === "long" && price <= liquidationPrice) ||
    (position.side === "short" && price >= liquidationPrice)
  ) {
    closeReason = "liquidation";
  }

  if (!closeReason) return;

  await closeDerivative(position.id, price, closeReason);
}

async  closeDerivative(positionId, price, reason) {
  const tx = await sequelize.transaction();

  try {
    const pos = await DerivativePosition.findByPk(positionId, {
      transaction: tx,
      lock: tx.LOCK.UPDATE
    });

    if (!pos || pos.status !== "open") {
      await tx.rollback();
      return;
    }

    const pnl =
      pos.side === "long"
        ? (price - pos.entryPrice) * pos.size
        : (pos.entryPrice - price) * pos.size;

    let payout = pos.margin + pnl;

    if (reason === "liquidation") {
      payout = 0; // user loses margin
    }

    if (payout > 0) {
      await applyLedgerEntry({
        userId: pos.userId,
        amount: payout,
        type: "credit",
        referenceId: pos.id,
        referenceType: "derivative",
        description: `Closed (${reason})`,
        transaction: tx
      });
    }

    await pos.update({
      status: "closed",
      exitPrice: price,
      profit: pnl,
      closeReason: reason
    }, { transaction: tx });

    await tx.commit();

  } catch (err) {
    await tx.rollback();
  }
}
 calculateLiquidationPrice(pos) {
  const maintenanceMargin = 0.5; // example %

  if (pos.side === "long") {
    return pos.entryPrice * (1 - (pos.margin / pos.notional) - maintenanceMargin);
  } else {
    return pos.entryPrice * (1 + (pos.margin / pos.notional) + maintenanceMargin);
  }
}

async  applyLedgerEntry({ userId, amount, type, referenceId, referenceType, description, transaction }) {
  const balance = await UserBalance.findOne({
    where: { userId },
    transaction,
    lock: transaction.LOCK.UPDATE
  });

  const before = Number(balance.balance || 0);
  const after = type === "credit" ? before + amount : before - amount;

  await LedgerEntry.create({
    user_id: userId,
    amount,
    type,
    balance_before: before,
    balance_after: after,
    reference_id: referenceId,
    reference_type: referenceType,
    description
  }, { transaction });

  await balance.update({ balance: after }, { transaction });
}



}
setInterval(tradeWatcher, 1000);
module.exports = new CentralService();
