import BreadCrumbLink from "@/layouts/shared/page.breadcrumbLink";
import { createRoleProtectedLoader } from "@/lib/features/protectedFetcher";
import { getPromoCodeByCode } from "@/lib/features/promo-codes/query";
import type { PromoCode } from "@/lib/features/promo-codes/types";
import { getPromoCodeUsers } from "@/lib/features/promo-code-campaigns/query";
import type { PromoCodeUsersResult } from "@/lib/features/promo-code-campaigns/types";
import type { AccessScope, CoreResponse } from "@/lib/features/types";
import { FEATURE_SLUGS, getCheckedAccess } from "@/lib/role-helpers";
import type { Route } from "./+types/page";
import PromoCodeDetailsClientPage from "./_client";
import { PromoThemeShell } from "@/routes/promo-code-campaigns/_components/PromoThemeShell";

/**
 * Updot's /v1/admin/promo-codes/:code can return the promo wrapped as
 * { success, data: <promo>, message } OR flat. Our core proxy preserves
 * whichever shape upstream sent. Normalize so the client always sees the
 * flat promo with fields like name/rewards/access_list/signupPromoCodes at
 * the root — otherwise mapPromoToForm() reads undefined for everything and
 * the edit modal appears empty.
 */
function unwrapPromo(raw: unknown): PromoCode | null {
  if (!raw || typeof raw !== "object") return null;
  const obj = raw as Record<string, unknown>;
  if (
    "data" in obj &&
    obj.data &&
    typeof obj.data === "object" &&
    !Array.isArray(obj.data) &&
    !("name" in obj || "code" in obj || "rewards" in obj)
  ) {
    return obj.data as PromoCode;
  }
  return obj as PromoCode;
}

export const handle = {
  breadcrumb: () => (
    <BreadCrumbLink
      links={[
        { label: "Promo Codes" },
        { label: "Campaigns", to: "/admin/promo-code-campaigns" },
        { label: "Promo Code" },
      ]}
    />
  ),
};

export const loader = createRoleProtectedLoader(
  FEATURE_SLUGS.promoCodeCampaigns,
  "read",
  async ({ request, params }) => {
    const { campaignId, promoCodeId } = params as {
      campaignId: string;
      promoCodeId: string;
    };
    const cookie = { Cookie: request.headers.get("Cookie") ?? "" };
    // Analytics is fetched client-side (skeleton) so navigation isn't blocked
    // by the slower aggregation; promo + first page of users load up front.
    const [promoResponse, usersResponse] = await Promise.all([
      getPromoCodeByCode(promoCodeId, cookie),
      getPromoCodeUsers(promoCodeId, { page: 1, pageSize: 100 }, cookie),
    ]);
    const accessScope = getCheckedAccess(request, FEATURE_SLUGS.promoCodes);
    return {
      campaignId,
      promoCodeId,
      promoResponse,
      usersResponse,
      accessScope,
    };
  },
);

const Page: React.FC<Route.ComponentProps> = ({ loaderData }) => {
  const { campaignId, promoCodeId, promoResponse, usersResponse, accessScope } =
    loaderData as unknown as {
      campaignId: string;
      promoCodeId: string;
      promoResponse: CoreResponse<PromoCode>;
      usersResponse: CoreResponse<PromoCodeUsersResult>;
      accessScope: AccessScope;
    };

  // These campaign detail routes are registered outside
  // promo-codes.layout.tsx, so the layout's theme does not reach them.
  // Mounting the shell here keeps them in step with the dashboard.
  return (
    <PromoThemeShell>
      <PromoCodeDetailsClientPage
        campaignId={campaignId}
        promoCodeId={promoCodeId}
        initialPromoCode={
          promoResponse?.success ? unwrapPromo(promoResponse.data) : null
        }
        initialAnalytics={null}
        initialUsers={
          usersResponse?.success
            ? usersResponse.data
            : {
                items: [],
                pagination: { page: 1, pageSize: 100, total: 0, totalPages: 1 },
              }
        }
        initialError={
          promoResponse?.success ? undefined : promoResponse?.message
        }
        accessScope={accessScope}
      />
    </PromoThemeShell>
  );
};

export default Page;
