import models from "$models/index.js";
import {
  accessGrantHelpers,
  identityHelpers,
  paymentCardHelpers,
} from "$helpers/index.js";

const isOwner = (batchUser, requestUser) => {
  const itemUserId = batchUser?._id || batchUser;
  return itemUserId?.toString() === requestUser?._id?.toString();
};

const attachRequestsToPaymentCards = async (paymentCards = []) => {
  const cardIds = paymentCards.map((item) => item?._id).filter(Boolean);
  if (!cardIds.length) {
    return paymentCards;
  }

  const requests = await models.request
    .find({ paymentCard: { $in: cardIds } })
    .select(
      "investor trackId fundName fundSeoRegisterNo status amountRial units unitPrice paymentMethod paymentGateway createdAt paymentCard",
    )
    .populate("investor", identityHelpers.USER_IDENTITY_PROJECTION)
    .lean();

  const requestByCardId = new Map(
    requests
      .filter((item) => item.paymentCard)
      .map((item) => [String(item.paymentCard), item]),
  );

  return paymentCards.map((item) => {
    const nextItem = item.toObject ? item.toObject() : item;
    nextItem.request = requestByCardId.get(String(item._id)) || null;
    return nextItem;
  });
};

export const ALL = async (req, res) => {
  try {
    const pageSize = req.query.pageSize ? parseInt(req.query.pageSize) : 20;
    const currentPage = req.query.page ? parseInt(req.query.page) : 1;
    const skip = pageSize * currentPage - pageSize;
    const query = {};
    const canAccessAllCards = accessGrantHelpers.hasAllResourceAccess(
      req.user,
      "paymentCards",
    );

    if (!canAccessAllCards) {
      const ownBatchIds = await models.paymentBatchCard
        .find({ user: req.user._id })
        .select("_id")
        .lean();
      query.batchId = { $in: ownBatchIds.map((item) => item._id) };
    }

    const paymentCards = await models.paymentCard
      .find(query)
      .limit(pageSize)
      .skip(skip)
      .populate({
        path: "batchId",
        populate: {
        path: "user",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
      })
      .sort({ createdAt: -1 });

    const enrichedPaymentCards = await attachRequestsToPaymentCards(paymentCards);

    const totalData = await models.paymentCard.countDocuments(query);
    const totalPage = pageSize ? Math.ceil(totalData / pageSize) : 0;

    return res.status(200).send({
      paymentCards: enrichedPaymentCards,
      totalData,
      currentPage,
      totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت کارت‌ها";
    return res.status(status).send({ message });
  }
};

export const SINGLE = async (req, res) => {
  try {
    const { id } = req.params;
    const paymentCard = await models.paymentCard
      .findById(id)
      .populate({
        path: "batchId",
        populate: {
        path: "user",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
      });

    if (!paymentCard) {
      return res.status(404).send({ message: "یافت نشد" });
    }

    if (
      !accessGrantHelpers.hasAllResourceAccess(req.user, "paymentCards") &&
      !isOwner(paymentCard.batchId?.user, req.user)
    ) {
      return res.status(403).send({ message: "دسترسی محدود" });
    }

    const [enrichedPaymentCard] = await attachRequestsToPaymentCards([
      paymentCard,
    ]);
    return res.status(200).send(enrichedPaymentCard);
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت کارت";
    return res.status(status).send({ message });
  }
};

export const CREATE = async (req, res) => {
  try {
    const amount = Number(req.body.amount || 0);
    const expiresAt = req.body.expiresAt ? new Date(req.body.expiresAt) : null;
    const title = String(req.body.title || "").trim() || "کارت هدیه تکی";
    const description = String(req.body.description || "").trim();
    const fundSeoRegisterNo = String(req.body.fundSeoRegisterNo || "").trim();
    const fundName = String(req.body.fundName || "").trim();

    if (!Number.isSafeInteger(amount) || amount <= 0) {
      return res.status(422).send({ message: "مبلغ کارت نامعتبر است" });
    }

    if (!expiresAt || Number.isNaN(expiresAt.getTime())) {
      return res.status(422).send({ message: "تاریخ انقضا نامعتبر است" });
    }

    if (expiresAt.getTime() <= Date.now()) {
      return res.status(422).send({ message: "تاریخ انقضا باید در آینده باشد" });
    }

    if (!fundSeoRegisterNo) {
      return res.status(422).send({ message: "صندوق کارت الزامی است" });
    }

    if (!fundName) {
      return res.status(422).send({ message: "نام صندوق کارت الزامی است" });
    }

    const batch = await models.paymentBatchCard.create({
      title,
      description,
      createMode: "single",
      quantity: 1,
      amount,
      totalAmount: amount,
      status: "active",
      user: req.user._id,
      expiresAt,
    });

    const [cardNumber] = await paymentCardHelpers.generateUniqueCardNumbers(1);
    const paymentCard = await models.paymentCard.create({
      batchId: batch._id,
      cardNumber,
      amount,
      fundSeoRegisterNo,
      fundName,
      status: "active",
      expiresAt,
    });

    return res.status(201).send({
      message: "کارت هدیه با موفقیت ایجاد شد",
      data: {
        batch,
        card: paymentCard,
      },
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در ایجاد کارت";
    return res.status(status).send({ message });
  }
};

export const DELETE = async (req, res) => {
  try {
    const { id } = req.params;
    const paymentCard = await models.paymentCard
      .findById(id)
      .populate({
        path: "batchId",
        select: "user status",
      });

    if (!paymentCard) {
      return res.status(404).send({ message: "یافت نشد" });
    }

    if (
      !accessGrantHelpers.hasAllResourceAccess(req.user, "paymentCards") &&
      !isOwner(paymentCard.batchId?.user, req.user)
    ) {
      return res.status(403).send({ message: "دسترسی محدود" });
    }

    if (paymentCard.status === "used") {
      return res.status(422).send({ message: "کارت استفاده شده قابل لغو نیست" });
    }

    if (paymentCard.status === "cancelled") {
      return res.status(200).send({ message: "این کارت قبلاً لغو شده است" });
    }

    paymentCard.status = "cancelled";
    paymentCard.cancelledAt = new Date();
    await paymentCard.save();
    await paymentCardHelpers.syncPaymentBatchStatus(paymentCard.batchId?._id);

    return res.status(200).send({
      message: "کارت با موفقیت لغو شد",
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در لغو کارت";
    return res.status(status).send({ message });
  }
};

export const LOOKUP = async (req, res) => {
  try {
    const cardNumber = paymentCardHelpers.normalizeCardNumber(
      req.params.cardNumber,
    );
    const fundSeoRegisterNo = String(req.query.fundSeoRegisterNo || "").trim();

    if (!/^\d{16}$/.test(cardNumber)) {
      return res.status(422).send({ message: "شماره کارت باید ۱۶ رقم باشد" });
    }

    const paymentCard = await models.paymentCard.findOne({ cardNumber }).lean();

    if (!paymentCard) {
      return res.status(404).send({ message: "کارت هدیه یافت نشد" });
    }

    if (paymentCard.status !== "active") {
      return res.status(422).send({ message: "این کارت قابل استفاده نیست" });
    }

    if (paymentCardHelpers.isCardExpired(paymentCard)) {
      return res.status(422).send({
        message: "تاریخ انقضای کارت به پایان رسیده است",
      });
    }

    if (
      fundSeoRegisterNo &&
      String(paymentCard.fundSeoRegisterNo || "").trim() !== fundSeoRegisterNo
    ) {
      return res.status(422).send({
        message: "این کارت هدیه فقط برای صندوق تخصیص‌یافته خودش قابل استفاده است",
      });
    }

    return res.status(200).send({
      cardNumber: paymentCard.cardNumber,
      amount: paymentCard.amount,
      fundSeoRegisterNo: paymentCard.fundSeoRegisterNo,
      fundName: paymentCard.fundName,
      status: paymentCard.status,
      expiresAt: paymentCard.expiresAt,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در بررسی کارت";
    return res.status(status).send({ message });
  }
};
