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

const parseNumericValue = (value) => {
  const parsedValue = Number(value);
  return Number.isFinite(parsedValue) ? parsedValue : 0;
};

const createTrackId = () =>
  `${new Date().toISOString().replace(/\D/g, "").slice(0, 14)}${String(
    Math.floor(Math.random() * 900) + 100,
  )}`;

const normalizePayload = (body = {}) => {
  const amountRial = parseNumericValue(body.amountRial ?? body.amount);
  const transferedAt = new Date(body.transferedAt);

  return {
    receiptFile: body.receiptFile || body.slipImage,
    fundSeoRegisterNo: String(body.fundSeoRegisterNo || "").trim(),
    fundName: String(body.fundName || body.fundTitle || "").trim(),
    sourceAccountNumber: String(body.sourceAccountNumber || "").trim(),
    destinationAccountNumber: String(
      body.destinationAccountNumber || "",
    ).trim(),
    sourceBankCode: String(body.sourceBankCode || "").trim(),
    sourceBankName: String(body.sourceBankName || "").trim(),
    sourceBankId: String(body.sourceBankId || "").trim(),
    transferedAt,
    amountRial,
    notes: String(body.notes || "").trim(),
  };
};

const validatePayload = (payload) => {
  const requiredFields = [
    "receiptFile",
    "fundSeoRegisterNo",
    "fundName",
    "sourceAccountNumber",
    "destinationAccountNumber",
    "sourceBankCode",
    "sourceBankId",
  ];

  const missingField = requiredFields.find((field) => !payload[field]);
  if (missingField) {
    return "اطلاعات فیش انتقال کامل نیست.";
  }

  if (!mongoose.Types.ObjectId.isValid(payload.receiptFile)) {
    return "تصویر فیش معتبر نیست.";
  }

  if (Number.isNaN(payload.transferedAt.getTime())) {
    return "تاریخ انتقال معتبر نیست.";
  }

  if (!Number.isSafeInteger(payload.amountRial) || payload.amountRial <= 0) {
    return "مبلغ انتقال باید عددی معتبر باشد.";
  }

  return "";
};

const resolveBankIdValue = (bank) => {
  const numericBankId = Number(bank?.sejamBankId);
  return Number.isFinite(numericBankId) ? numericBankId : null;
};

const populatePaymentSlip = (query) =>
  query
    .populate({
      path: "request",
      populate: {
        path: "investor",
        select: identityHelpers.USER_IDENTITY_PROJECTION,
      },
    })
    .populate("receiptFile", "type extension createdAt");

const isOwner = (paymentSlip, requestUser) => {
  const investor = paymentSlip?.request?.investor;
  const investorId = investor?._id || investor;
  return investorId?.toString() === requestUser?._id?.toString();
};

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 canAccessAllPaymentSlips = accessGrantHelpers.hasAllResourceAccess(
      req.user,
      "paymentSlips",
    );

    if (!canAccessAllPaymentSlips) {
      const ownRequestIds = await models.request
        .find({ investor: req.user._id, paymentSlip: { $exists: true } })
        .select("paymentSlip")
        .lean();

      query._id = {
        $in: ownRequestIds.map((item) => item.paymentSlip).filter(Boolean),
      };
    }

    const paymentSlips = await populatePaymentSlip(
      models.paymentSlip.find(query),
    )
      .limit(pageSize)
      .skip(skip)
      .sort({ createdAt: -1 });

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

    res.status(200).send({
      paymentSlips,
      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 data = await populatePaymentSlip(models.paymentSlip.findById(id));

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

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

    res.status(200).send(data);
  } 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 payload = normalizePayload(req.body);
    const validationMessage = validatePayload(payload);

    if (validationMessage) {
      return res.status(422).send({ message: validationMessage });
    }

    const file = await models.file.findById(payload.receiptFile);
    if (!file) {
      return res.status(404).send({ message: "فایل رسید یافت نشد." });
    }

    const bank = await models.bank.findOne({
      $or: [
        { sejamBankId: payload.sourceBankId },
        { bankCode: payload.sourceBankCode },
      ],
    });

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

    payload.sourceBankId = bank.sejamBankId;
    payload.sourceBankCode = bank.bankCode;
    payload.sourceBankName = bank.name;

    const trackId = createTrackId();

    const createdRequest = await requestHelpers.createRequestWithHistory(
      {
        investor: req.user._id,
        fundSeoRegisterNo: payload.fundSeoRegisterNo,
        fundName: payload.fundName,
        trackId,
        operationType: "issuance",
        paymentMethod: "slip",
        amountRial: payload.amountRial,
        units: 0,
        unitPrice: 0,
        status: "wait_for_payment",
        meta: {
          source: "payment-slip",
          BankBillCode: trackId,
          FundBankAccountNumber: payload.destinationAccountNumber,
          sourceAccountNumber: payload.sourceAccountNumber,
          sourceBankCode: payload.sourceBankCode,
          sourceBankId: payload.sourceBankId,
          transferedAt: payload.transferedAt,
          notes: payload.notes,
        },
      },
      {
        actorUser: req.user,
        source: "payment_slip",
        eventType: "created",
      },
    );

    let createdPaymentSlip;
    try {
      createdPaymentSlip = await models.paymentSlip.create({
        ...payload,
        request: createdRequest._id,
      });
    } catch (error) {
      await requestHelpers.deleteRequestWithHistory(createdRequest._id, {
        actorUser: req.user,
        source: "payment_slip",
        eventType: "deleted",
      });
      throw error;
    }

    await requestHelpers.updateRequestWithHistory(
      createdRequest,
      {
        paymentSlip: createdPaymentSlip._id,
        status: "reviewing",
      },
      {
        actorUser: req.user,
        source: "payment_slip",
        eventType: "payment_slip_attached",
      },
    );

    await models.userInfo.findOneAndUpdate(
      { user: req.user._id },
      {
        $set: {
          "bankAccount.accountNumber": payload.sourceAccountNumber,
          "bankAccount.bankName": bank.name,
          "bankAccount.bankId": resolveBankIdValue(bank),
          "bankAccount.lastModified": new Date(),
        },
        $setOnInsert: {
          user: req.user._id,
        },
      },
      { new: true, upsert: true },
    );

    const populated = await populatePaymentSlip(
      models.paymentSlip.findById(createdPaymentSlip._id),
    );
    const syncedRequest = await requestHelpers.submitTadbirIssuanceForRequest(
      createdRequest._id,
    );

    res.status(201).send({
      message:
        syncedRequest?.status === "failed"
          ? "فیش پرداخت ثبت شد، اما ارسال درخواست صدور به تدبیر ناموفق بود."
          : "فیش پرداخت با موفقیت ثبت شد.",
      data: populated,
      request: syncedRequest || createdRequest,
      externalSubmissionSucceeded: syncedRequest?.status !== "failed",
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در ثبت فیش پرداخت";
    return res.status(status).send({ message });
  }
};

export const UPDATE = async (req, res) => {
  try {
    const { id } = req.params;
    const updateData = { ...req.body };

    delete updateData.request;
    delete updateData._id;
    delete updateData.createdAt;
    delete updateData.updatedAt;

    if (
      updateData.receiptFile === undefined &&
      updateData.slipImage !== undefined
    ) {
      updateData.receiptFile = updateData.slipImage;
    }
    delete updateData.slipImage;

    if (
      updateData.fundName === undefined &&
      updateData.fundTitle !== undefined
    ) {
      updateData.fundName = updateData.fundTitle;
    }
    delete updateData.fundTitle;

    if (
      updateData.amountRial === undefined &&
      updateData.amount !== undefined
    ) {
      updateData.amountRial = updateData.amount;
    }
    delete updateData.amount;

    if (updateData.amountRial !== undefined) {
      updateData.amountRial = parseNumericValue(updateData.amountRial);
    }

    if (updateData.transferedAt) {
      updateData.transferedAt = new Date(updateData.transferedAt);
    }

    const existingItem = await populatePaymentSlip(
      models.paymentSlip.findById(id),
    );
    if (!existingItem) {
      return res.status(404).send({ message: "یافت نشد" });
    }

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

    const updated = await populatePaymentSlip(
      models.paymentSlip.findByIdAndUpdate(
        id,
        { $set: updateData },
        { new: true, runValidators: true },
      ),
    );

    if (updated?.request?._id) {
      await requestHelpers.updateRequestWithHistory(
        updated.request._id,
        {
          fundSeoRegisterNo: updated.fundSeoRegisterNo,
          fundName: updated.fundName,
          amountRial: updated.amountRial,
        },
        {
          actorUser: req.user,
          source: "payment_slip",
          eventType: "updated",
        },
      );
    }

    res.status(200).send({
      message: "با موفقیت ویرایش شد",
      data: updated,
    });
  } 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 existingItem = await populatePaymentSlip(
      models.paymentSlip.findById(id),
    );

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

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

    await models.paymentSlip.findByIdAndDelete(id);

    if (existingItem.request?._id) {
      await requestHelpers.deleteRequestWithHistory(existingItem.request._id, {
        actorUser: req.user,
        source: "payment_slip",
        eventType: "deleted",
      });
    }

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