import models from "$models/index.js";
import { accessGrantHelpers, identityHelpers } from "$helpers/index.js";
import { tadbirServices } from "$services/index.js";
import { isSepahBrokerFund } from "$services/sepah/sepah-broker.helper.js";

const CERTIFICATE_TRACK_ID_START = 100000;
const TEMP_DISABLED_CERTIFICATE_MESSAGE =
  "صدور گواهی برای صندوق‌های سپه موقتاً غیرفعال است.";

const formatCertificateDate = (value) => {
  const parsedValue = new Date(value);
  if (Number.isNaN(parsedValue.getTime())) {
    return "";
  }

  return new Intl.DateTimeFormat("fa-IR-u-nu-latn", {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).format(parsedValue);
};

const buildCertificateListQuery = (query, { pageSize, skip }) =>
  models.certificate
    .find(query)
    .select("-rawPayload -download.bodyBase64")
    .limit(pageSize)
    .skip(skip)
    .populate("investor", identityHelpers.USER_IDENTITY_PROJECTION)
    .sort({ createdAt: -1 });

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

const extractCertificateDocument = ({
  investorId,
  fundSeoRegisterNo,
  fundName,
  certificateTrackId,
  payload,
  downloadMeta,
  renderedText,
  createdAt,
  updatedAt,
}) => ({
  investor: investorId,
  fundSeoRegisterNo,
  fundName: fundName || payload?.fundTitle || "",
  certificateTrackId,
  ...payload,
  renderedText: renderedText || "",
  download: downloadMeta || undefined,
  rawPayload: payload,
  createdAt,
  updatedAt,
});

const generateNextCertificateTrackId = async () => {
  const latestCertificate = await models.certificate
    .findOne({
      certificateTrackId: { $regex: "^\\d+$" },
    })
    .sort({ certificateTrackId: -1 })
    .select("certificateTrackId")
    .lean();

  const latestTrackId = Number(latestCertificate?.certificateTrackId || 0);
  const nextTrackId = Number.isFinite(latestTrackId)
    ? Math.max(CERTIFICATE_TRACK_ID_START, latestTrackId + 1)
    : CERTIFICATE_TRACK_ID_START;

  return String(nextTrackId);
};

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

    if (!canAccessAllCertificates) {
      query.investor = req.user._id;
    }

    const certificates = await buildCertificateListQuery(query, {
      pageSize,
      skip,
    });

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

    res.status(200).send({
      certificates,
      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 certificate = await models.certificate
      .findById(id)
      .populate("investor", identityHelpers.USER_IDENTITY_PROJECTION);

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

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

    return res.status(200).send(certificate);
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت گواهی سرمایه‌گذاری";
    return res.status(status).send({ message });
  }
};

export const DOWNLOAD = async (req, res) => {
  try {
    const { id } = req.params;
    const certificate = await models.certificate
      .findById(id)
      .populate("investor", identityHelpers.USER_IDENTITY_PROJECTION);

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

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

    const bodyBase64 = certificate.download?.bodyBase64;
    if (!bodyBase64) {
      return res.status(404).send({ message: "فایل گواهی موجود نیست." });
    }

    const fileBuffer = Buffer.from(bodyBase64, "base64");
    const fileName =
      certificate.download?.fileName ||
      `certificate-${certificate.certificateTrackId || certificate._id}.pdf`;

    res.setHeader(
      "Content-Type",
      certificate.download?.contentType || "application/pdf",
    );
    res.setHeader(
      "Content-Disposition",
      `attachment; filename="${encodeURIComponent(fileName)}"`,
    );

    return res.status(200).send(fileBuffer);
  } 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 nationalCode = req.identifier;

    if (!nationalCode) {
      return res.status(400).send({
        message: "شناسه هویتی کاربر برای دریافت گواهی سرمایه‌گذاری یافت نشد.",
      });
    }

    const { fundSeoRegisterNo, fundName } = req.body || {};

    if (!fundSeoRegisterNo) {
      return res.status(400).send({ message: "شناسه ثبت صندوق الزامی است." });
    }

    const fundsResponse = await tadbirServices.getFunds();
    const funds = Array.isArray(fundsResponse?.items) ? fundsResponse.items : [];
    const matchedFund = funds.find((fund) => {
      const fundRegisterCode = String(fund?.fundRegisterCode || "").trim();
      const seoRegisterNo = String(fund?.nav?.seoRegisterNo || "").trim();
      const normalizedInput = String(fundSeoRegisterNo || "").trim();

      return (
        normalizedInput === fundRegisterCode || normalizedInput === seoRegisterNo
      );
    });

    if (isSepahBrokerFund(matchedFund?.fundRegisterCode || fundSeoRegisterNo)) {
      return res.status(422).send({
        message: TEMP_DISABLED_CERTIFICATE_MESSAGE,
      });
    }

    const certificateTrackId = await generateNextCertificateTrackId();
    const certificateCreatedAt = new Date();

    const generatedCertificate = await tadbirServices.generateCertificate({
      fundSeoRegisterNo,
      nationalCode,
      certificateTrackId,
      createdAt: certificateCreatedAt,
    });

    const payload = {
      ...(generatedCertificate.payload || {}),
      workingDate: formatCertificateDate(certificateCreatedAt),
      requestDate: formatCertificateDate(certificateCreatedAt),
    };

    const newItem = await models.certificate.create(
      extractCertificateDocument({
        investorId: req.user._id,
        fundSeoRegisterNo,
        fundName,
        certificateTrackId,
        payload,
        downloadMeta: generatedCertificate.downloadMeta,
        renderedText: generatedCertificate.renderedText,
        createdAt: certificateCreatedAt,
        updatedAt: certificateCreatedAt,
      }),
    );
    const fileName =
      generatedCertificate.downloadMeta?.fileName ||
      `certificate-${certificateTrackId}.pdf`;
    const pdfBodyBase64 = generatedCertificate.downloadMeta?.bodyBase64 || "";

    res.setHeader(
      "Content-Type",
      generatedCertificate.downloadMeta?.contentType || "application/pdf",
    );
    res.setHeader(
      "Content-Disposition",
      `attachment; filename="${encodeURIComponent(fileName)}"`,
    );
    res.setHeader("X-Certificate-Id", String(newItem._id));
    res.setHeader("X-Certificate-Track-Id", String(certificateTrackId));

    return res.status(201).send(Buffer.from(pdfBodyBase64, "base64"));
  } catch (error) {
    const message =
      error?.response?.data?.errorMessage ||
      error?.response?.data?.message ||
      error.message ||
      "خطا در ایجاد گواهی سرمایه‌گذاری";
    const status = error.response?.status || error.status || 500;

    return res.status(status).send({ message });
  }
};
