import { appConfig } from "$config/index.js";
import { clubHelpers, identityHelpers } from "$helpers/index.js";
import models from "$models/index.js";

const DEFAULT_PAGE_SIZE = 20;

const parsePage = (value, fallback = 1) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed > 0 ? Math.floor(parsed) : fallback;
};

const parsePageSize = (value, fallback = DEFAULT_PAGE_SIZE) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed > 0
    ? Math.min(100, Math.floor(parsed))
    : fallback;
};

const parseInteger = (value, fallback = 0) => {
  const parsed = Number(value);
  return Number.isFinite(parsed) ? Math.round(parsed) : fallback;
};

const paginateQuery = async ({
  query,
  page,
  pageSize,
  sort = { createdAt: -1 },
  populate = [],
}) => {
  let cursor = query
    .sort(sort)
    .skip((page - 1) * pageSize)
    .limit(pageSize);

  for (const populateItem of populate) {
    cursor = cursor.populate(populateItem);
  }

  const [items, totalData] = await Promise.all([
    cursor.lean(),
    query.model.countDocuments(query.getFilter()),
  ]);

  return {
    items,
    totalData,
    currentPage: page,
    totalPage: Math.max(1, Math.ceil(totalData / pageSize)),
  };
};

const getBaseUrl = (req) => String(appConfig.frontBaseUrl || "").trim();

const buildRewardPresentation = (reward) => {
  const batch =
    reward?.paymentBatch && typeof reward.paymentBatch === "object"
      ? reward.paymentBatch
      : null;
  const amount = parseInteger(batch?.amount);
  const fundName = String(batch?.fundName || "").trim();
  const fundSeoRegisterNo = String(batch?.fundSeoRegisterNo || "").trim();

  return {
    title:
      fundName && amount
        ? `کارت هدیه ${fundName}`
        : amount
          ? `کارت هدیه ${amount.toLocaleString("fa-IR")} ریالی`
          : "کارت هدیه باشگاه",
    description: fundName
      ? `کارت هدیه ${amount.toLocaleString("fa-IR")} ریالی صندوق ${fundName}`
      : "جایزه باشگاه مشتریان",
    meta: {
      amount,
      fundName,
      fundSeoRegisterNo,
      expiresAt: batch?.expiresAt || null,
    },
  };
};

const buildMemberRewardPayload = (reward, currentPoints) => ({
  ...reward,
  ...buildRewardPresentation(reward),
  canRedeem: currentPoints >= parseInteger(reward?.pointsCost),
});

const normalizeRewardBatchPayload = (body = {}) => {
  const amount = parseInteger(body.amount);
  const fundSeoRegisterNo = String(body.fundSeoRegisterNo || "").trim();
  const fundName = String(body.fundName || "").trim();
  const expiresAt = body.expiresAt ? new Date(body.expiresAt) : null;

  if (amount <= 0) {
    throw { status: 422, message: "مبلغ کارت هدیه نامعتبر است." };
  }

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

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

  if (!expiresAt || Number.isNaN(expiresAt.getTime())) {
    throw { status: 422, message: "تاریخ انقضای کارت هدیه نامعتبر است." };
  }

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

  return {
    amount,
    fundSeoRegisterNo,
    fundName,
    expiresAt,
  };
};

const buildRewardBatchTemplate = ({ body = {}, userId }) => {
  const normalizedBatch = normalizeRewardBatchPayload(body);

  return {
    title: `جایزه باشگاه - ${normalizedBatch.fundName}`,
    description: "قالب کارت هدیه برای جایزه باشگاه مشتریان",
    createMode: "single",
    usageContext: "club_reward",
    quantity: 1,
    amount: normalizedBatch.amount,
    fundSeoRegisterNo: normalizedBatch.fundSeoRegisterNo,
    fundName: normalizedBatch.fundName,
    totalAmount: normalizedBatch.amount,
    status: "active",
    user: userId,
    expiresAt: normalizedBatch.expiresAt,
  };
};

export const ME = async (req, res) => {
  try {
    const snapshot = await clubHelpers.getUserClubSnapshot(req.user);
    const currentPoints = parseInteger(snapshot?.currentPoints);
    const referralCode = snapshot?.referralCode || "";
    const latestRedemptions = (snapshot?.latestRedemptions || []).map((item) => ({
      ...item,
      reward: item?.reward
        ? {
            ...item.reward,
            ...buildRewardPresentation(item.reward),
          }
        : item?.reward,
    }));

    return res.status(200).send({
      message: "باشگاه مشتریان دریافت شد",
      data: {
        isPrivatePerson: req.user?.isPrivatePerson !== false,
        currentPoints,
        successfulReferralsCount: parseInteger(
          snapshot?.successfulReferralsCount,
        ),
        referralCode,
        referralLink: referralCode
          ? `${getBaseUrl(req)}/omidex?ref=${encodeURIComponent(referralCode)}`
          : "",
        latestRedemptions,
      },
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت داشبورد باشگاه";
    return res.status(status).send({ message });
  }
};

export const HISTORY = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const result = await paginateQuery({
      query: models.clubPointTransaction.find({ user: req.user._id }),
      page,
      pageSize,
    });

    return res.status(200).send({
      history: result.items,
      totalData: result.totalData,
      currentPage: result.currentPage,
      totalPage: result.totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت تاریخچه امتیاز";
    return res.status(status).send({ message });
  }
};

export const REFERRALS = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const result = await paginateQuery({
      query: models.clubReferral.find({ inviter: req.user._id }),
      page,
      pageSize,
      populate: [
        {
          path: "invitee",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
      ],
    });

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

export const REWARDS = async (req, res) => {
  try {
    const currentPoints = await clubHelpers.getUserPointBalance(req.user._id);
    const rewards = await models.clubReward
      .find({ status: "active" })
      .populate("paymentBatch")
      .sort({ pointsCost: 1, createdAt: -1 })
      .lean();

    return res.status(200).send({
      rewards: rewards.map((reward) =>
        buildMemberRewardPayload(reward, currentPoints),
      ),
      currentPoints,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت جوایز";
    return res.status(status).send({ message });
  }
};

export const REDEEM_REWARD = async (req, res) => {
  try {
    const reward = await models.clubReward
      .findById(req.params.id)
      .populate("paymentBatch");

    if (!reward || reward.status !== "active") {
      return res.status(404).send({ message: "جایزه یافت نشد" });
    }

    const redemption = await clubHelpers.fulfillRewardRedemption({
      user: req.user,
      reward,
    });

    return res.status(201).send({
      message: "جایزه با موفقیت ثبت و صادر شد",
      data: await models.clubRewardRedemption
        .findById(redemption._id)
        .populate("reward")
        .populate("paymentCard")
        .lean(),
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در بازخرید جایزه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_LEDGER = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const query = {};

    if (req.query.userId) {
      query.user = req.query.userId;
    }

    if (req.query.type) {
      query.type = String(req.query.type).trim();
    }

    if (req.query.source) {
      query.source = String(req.query.source).trim();
    }

    const result = await paginateQuery({
      query: models.clubPointTransaction.find(query),
      page,
      pageSize,
      populate: [
        {
          path: "user",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
      ],
    });

    return res.status(200).send({
      ledger: result.items,
      totalData: result.totalData,
      currentPage: result.currentPage,
      totalPage: result.totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت دفتر امتیاز باشگاه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_REFERRALS = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const query = {};

    if (req.query.status) {
      query.status = String(req.query.status).trim();
    }

    const result = await paginateQuery({
      query: models.clubReferral.find(query),
      page,
      pageSize,
      populate: [
        {
          path: "inviter",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
        {
          path: "invitee",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
      ],
    });

    return res.status(200).send({
      referrals: result.items,
      totalData: result.totalData,
      currentPage: result.currentPage,
      totalPage: result.totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت معرفی‌های باشگاه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_REDEMPTIONS = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const query = {};

    if (req.query.status) {
      query.status = String(req.query.status).trim();
    }

    const result = await paginateQuery({
      query: models.clubRewardRedemption.find(query),
      page,
      pageSize,
      populate: [
        {
          path: "user",
          select: identityHelpers.USER_IDENTITY_PROJECTION,
        },
        {
          path: "reward",
        },
        {
          path: "paymentCard",
        },
      ],
    });

    return res.status(200).send({
      redemptions: result.items,
      totalData: result.totalData,
      currentPage: result.currentPage,
      totalPage: result.totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت بازخریدهای باشگاه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_REWARDS = async (req, res) => {
  try {
    const page = parsePage(req.query.page);
    const pageSize = parsePageSize(req.query.pageSize);
    const query = {};

    if (req.query.status) {
      query.status = String(req.query.status).trim();
    }

    if (req.query.rewardType) {
      query.rewardType = String(req.query.rewardType).trim();
    }

    const result = await paginateQuery({
      query: models.clubReward.find(query),
      page,
      pageSize,
      sort: { createdAt: -1 },
      populate: [{ path: "paymentBatch" }],
    });

    return res.status(200).send({
      rewards: result.items.map((reward) => ({
        ...reward,
        ...buildRewardPresentation(reward),
      })),
      totalData: result.totalData,
      currentPage: result.currentPage,
      totalPage: result.totalPage,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در دریافت جوایز باشگاه";
    return res.status(status).send({ message });
  }
};

const normalizeRewardPayload = (body = {}) => {
  const pointsCost = parseInteger(body.pointsCost);

  if (pointsCost <= 0) {
    throw { status: 422, message: "هزینه امتیاز جایزه نامعتبر است." };
  }

  return {
    pointsCost,
    status:
      String(body.status || "active").trim() === "inactive"
        ? "inactive"
        : "active",
    rewardType: "payment_card",
  };
};

export const ADMIN_CREATE_REWARD = async (req, res) => {
  try {
    const payload = normalizeRewardPayload(req.body);
    const batch = await models.paymentBatchCard.create(
      buildRewardBatchTemplate({
        body: req.body,
        userId: req.user._id,
      }),
    );
    const reward = await models.clubReward.create({
      ...payload,
      paymentBatch: batch._id,
    });

    return res.status(201).send({
      message: "جایزه با موفقیت ایجاد شد",
      data: await models.clubReward
        .findById(reward._id)
        .populate("paymentBatch"),
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در ایجاد جایزه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_UPDATE_REWARD = async (req, res) => {
  try {
    const payload = normalizeRewardPayload(req.body);
    const reward = await models.clubReward.findById(req.params.id);

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

    const batchPayload = buildRewardBatchTemplate({
      body: req.body,
      userId: reward.paymentBatch ? undefined : req.user._id,
    });

    let batch = null;

    if (reward.paymentBatch) {
      batch = await models.paymentBatchCard.findByIdAndUpdate(
        reward.paymentBatch,
        {
          $set: {
            ...batchPayload,
            user: batchPayload.user || req.user._id,
          },
        },
        { new: true },
      );
    }

    if (!batch) {
      batch = await models.paymentBatchCard.create(
        buildRewardBatchTemplate({
          body: req.body,
          userId: req.user._id,
        }),
      );
      reward.paymentBatch = batch._id;
    }

    reward.set(payload);
    await reward.save();

    return res.status(200).send({
      message: "جایزه با موفقیت به‌روزرسانی شد",
      data: await models.clubReward
        .findById(reward._id)
        .populate("paymentBatch"),
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در به‌روزرسانی جایزه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_DELETE_REWARD = async (req, res) => {
  try {
    const reward = await models.clubReward.findByIdAndUpdate(
      req.params.id,
      {
        $set: {
          status: "inactive",
        },
      },
      { new: true },
    );

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

    return res.status(200).send({
      message: "جایزه غیرفعال شد",
      data: reward,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در غیرفعال‌سازی جایزه";
    return res.status(status).send({ message });
  }
};

export const ADMIN_CREATE_ADJUSTMENT = async (req, res) => {
  try {
    const userId = String(req.body.userId || "").trim();
    const points = parseInteger(req.body.points);
    const reason = String(req.body.reason || "").trim();

    if (!userId) {
      return res.status(422).send({ message: "کاربر الزامی است." });
    }

    if (!points) {
      return res.status(422).send({ message: "امتیاز اصلاحی نامعتبر است." });
    }

    const user = await models.user.findById(userId).select("_id");
    if (!user) {
      return res.status(404).send({ message: "کاربر یافت نشد" });
    }

    const eventKey = `club:manual-adjustment:${userId}:${Date.now()}`;
    let transaction = null;

    if (points > 0) {
      transaction = await clubHelpers.createPointCredit({
        userId,
        points,
        source: "manual_adjustment",
        sourceId: req.user._id,
        eventKey,
        meta: {
          reason,
          operatorUserId: req.user._id,
        },
      });
    } else {
      transaction = await clubHelpers.createSpendTransaction({
        userId,
        points: Math.abs(points),
        source: "manual_adjustment",
        sourceId: req.user._id,
        eventKey,
        meta: {
          reason,
          operatorUserId: req.user._id,
        },
      });
    }

    return res.status(201).send({
      message: "اصلاح امتیاز با موفقیت ثبت شد",
      data: transaction,
    });
  } catch (error) {
    const status = error.status || 500;
    const message = error.message || "خطا در ثبت اصلاح امتیاز";
    return res.status(status).send({ message });
  }
};
