import type { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import type { IPromotionModuleService, PromotionTypes } from "@medusajs/framework/types";

type GeneratePromoBody = {
  code?: string;
  prefix?: string;
  length?: number;
  valueType?: "percentage" | "fixed";
  value?: number;
  currencyCode?: string;
  targetType?: "items" | "order" | "shipping_methods";
  allocation?: "each" | "across";
  startsAt?: string | null;
  endsAt?: string | null;
  usageLimit?: number | null;
  minimumSubtotal?: number | null;
  regionIds?: string[];
  isAutomatic?: boolean;
  status?: "draft" | "active" | "inactive";
};

function randomCodePart(length: number) {
  const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  let out = "";
  for (let i = 0; i < length; i++) {
    out += chars[Math.floor(Math.random() * chars.length)];
  }
  return out;
}

async function ensureUniqueCode(
  promotionService: IPromotionModuleService,
  desiredCode: string,
) {
  let candidate = desiredCode.toUpperCase().replace(/[^A-Z0-9_-]/g, "");

  if (!candidate) {
    candidate = `PROMO-${randomCodePart(8)}`;
  }

  for (let i = 0; i < 20; i++) {
    const existing = await promotionService.listPromotions({ code: candidate });
    if (!existing.length) {
      return candidate;
    }
    candidate = `${candidate}-${randomCodePart(4)}`;
  }

  throw new Error("Unable to generate a unique promotion code.");
}

export async function POST(
  req: AuthenticatedMedusaRequest<GeneratePromoBody>,
  res: MedusaResponse,
) {
  const promotionService: IPromotionModuleService = req.scope.resolve("promotion");
  const body = (req.body ?? {}) as GeneratePromoBody;

  const valueType = body.valueType ?? "percentage";
  const targetType = body.targetType ?? "items";
  const allocation = body.allocation ?? "across";
  const status = body.status ?? "active";
  const value = Number(body.value ?? 0);

  if (!Number.isFinite(value) || value <= 0) {
    return res.status(400).json({ message: "value must be a positive number." });
  }

  if (valueType === "percentage" && value > 100) {
    return res.status(400).json({ message: "percentage value must be <= 100." });
  }

  if (valueType === "fixed" && !body.currencyCode) {
    return res.status(400).json({ message: "currencyCode is required for fixed promotions." });
  }

  const regionIds = (body.regionIds || []).filter(Boolean);
  const minSubtotal = body.minimumSubtotal ?? null;
  const startsAt = body.startsAt ? new Date(body.startsAt) : null;
  const endsAt = body.endsAt ? new Date(body.endsAt) : null;

  if (startsAt && Number.isNaN(startsAt.getTime())) {
    return res.status(400).json({ message: "startsAt must be a valid ISO date string." });
  }

  if (endsAt && Number.isNaN(endsAt.getTime())) {
    return res.status(400).json({ message: "endsAt must be a valid ISO date string." });
  }

  if (startsAt && endsAt && endsAt <= startsAt) {
    return res.status(400).json({ message: "endsAt must be later than startsAt." });
  }

  const codeSeed =
    body.code ||
    `${body.prefix?.toUpperCase().replace(/[^A-Z0-9_-]/g, "") || "PROMO"}-${randomCodePart(
      Math.max(4, Math.min(16, Number(body.length ?? 8))),
    )}`;
  const code = await ensureUniqueCode(promotionService, codeSeed);

  const rules: PromotionTypes.CreatePromotionRuleDTO[] = [];

  if (regionIds.length) {
    rules.push({
      attribute: "region_id",
      operator: "in",
      values: regionIds,
    });
  }

  if (typeof minSubtotal === "number" && Number.isFinite(minSubtotal) && minSubtotal > 0) {
    rules.push({
      attribute: "order_total",
      operator: "gte",
      values: [String(Math.floor(minSubtotal))],
    });
  }

  const payload: PromotionTypes.CreatePromotionDTO = {
    code,
    type: "standard",
    status,
    is_automatic: Boolean(body.isAutomatic),
    limit: body.usageLimit ?? null,
    application_method: {
      type: valueType,
      target_type: targetType,
      allocation,
      value,
      currency_code: valueType === "fixed" ? body.currencyCode : undefined,
    },
    rules: rules.length ? rules : undefined,
    campaign: startsAt || endsAt
      ? {
          name: `Campaign ${code}`,
          campaign_identifier: `CMP-${code}`,
          starts_at: startsAt,
          ends_at: endsAt,
        }
      : undefined,
  };

  const promotion = await promotionService.createPromotions(payload);

  return res.status(201).json({
    message: "Promotion created.",
    promotion: {
      id: promotion.id,
      code: promotion.code,
      type: promotion.type,
      status: promotion.status,
      is_automatic: promotion.is_automatic,
      limit: promotion.limit,
      campaign_id: promotion.campaign_id,
      application_method: promotion.application_method,
      rules: promotion.rules || [],
    },
  });
}
