"use client";

import { useCallback, useEffect, useState, type ComponentType, type FormEvent, type SVGProps } from "react";
import { toast } from "sonner";
import {
  ArrowLeftIcon,
  ArrowRightCircleIcon,
  ArrowTopRightOnSquareIcon,
  BanknotesIcon,
  BuildingStorefrontIcon,
  CalendarDaysIcon,
  CheckCircleIcon,
  CheckIcon,
  ClockIcon,
  EnvelopeOpenIcon,
  GlobeAltIcon,
  PaperAirplaneIcon,
  XCircleIcon,
  XMarkIcon,
} from "@heroicons/react/24/outline";
import { Button } from "@/components/Button";
import { CampaignAudienceSection } from "@/components/dashboard/campaign-detail/CampaignAudienceSection";
import { CampaignBriefDeliverables } from "@/components/dashboard/campaign-detail/CampaignBriefDeliverables";
import { CampaignDetailSkeleton } from "@/components/dashboard/campaign-detail/CampaignDetailSkeleton";
import { CampaignScheduleCard } from "@/components/dashboard/campaign-detail/CampaignStatusSchedule";
import { DashboardPageContainer } from "@/components/dashboard/DashboardPageContainer";
import { DashboardShell } from "@/components/dashboard/DashboardShell";
import { ErrorState } from "@/components/ui/ErrorState";
import { textareaClassName } from "@/components/ui/field-styles";
import { SectionHeader } from "@/components/ui/PageHeader";
import { StatusBadge } from "@/components/ui/StatusBadge";
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
import { ApiError } from "@/lib/api";
import { applicationApi, invitationApi, opportunityApi } from "@/lib/api/campaign-participation";
import {
  opportunityActionState,
  participantWorkspacePath,
  resolveOpportunityNextAction,
  type OpportunityActionState,
} from "@/lib/campaign-opportunity";
import { buildCampaignAudienceView, campaignDeliverableUnitCount } from "@/lib/campaign-detail-ui";
import {
  applicationStatusBadgeVariant,
  applicationStatusLabel,
  defaultApplicationMessage,
  invitationStatusBadgeVariant,
  invitationStatusLabel,
} from "@/lib/campaign-participation";
import { campaignStatusBadgeVariant, campaignStatusLabel } from "@/lib/campaigns";
import { formatDateRange, formatMoneyMinor } from "@/lib/format";
import { socialProviderLabel } from "@/lib/social-providers";
import type { CampaignOpportunity } from "@/types/campaign-participation";

const CREATOR_ONLY = ["creator"] as const;

type HeroIcon = ComponentType<SVGProps<SVGSVGElement>>;

function brandLabel(opportunity: CampaignOpportunity): string {
  const brand = opportunity.brand;
  if (!brand) return "Brand";
  return brand.company_name?.trim() || brand.username || "Brand";
}

function nextActionIcon(kind: OpportunityActionState["kind"]): HeroIcon {
  switch (kind) {
    case "participating":
      return ArrowRightCircleIcon;
    case "invitation_pending":
      return EnvelopeOpenIcon;
    case "accepted":
      return CheckCircleIcon;
    case "application_pending":
      return ClockIcon;
    case "application_rejected":
      return XCircleIcon;
    case "apply":
    default:
      return PaperAirplaneIcon;
  }
}

function FactChip({
  icon: Icon,
  label,
}: {
  icon: HeroIcon;
  label: string;
}) {
  return (
    <span className="inline-flex items-center gap-1.5 text-[0.8125rem] text-ink-60">
      <Icon className="size-4 shrink-0 text-ink-40" aria-hidden="true" />
      <span>{label}</span>
    </span>
  );
}

function OpportunityCompensationPanel({ opportunity }: { opportunity: CampaignOpportunity }) {
  return (
    <section className="space-y-3" aria-labelledby="opportunity-compensation-heading">
      <SectionHeader
        id="opportunity-compensation-heading"
        title="Compensation"
        icon={<BanknotesIcon className="size-4" />}
      />
      <div className="space-y-3 rounded-pv border border-line bg-surface px-3 py-3">
        <div className="flex items-baseline justify-between gap-3">
          <span className="text-[0.8125rem] text-ink-60">Campaign budget</span>
          <span className="text-[0.9375rem] font-semibold tabular-nums text-primary">
            {formatMoneyMinor(opportunity.budget_amount, opportunity.currency)}
          </span>
        </div>
        <p className="border-t border-line pt-2.5 text-[0.75rem] leading-snug text-ink-40">
          This is the brand’s campaign budget, not your individual payout. Your rate is confirmed when
          you join.
        </p>
      </div>
    </section>
  );
}

function OpportunityParticipatePanel({
  opportunity,
  onApplied,
}: {
  opportunity: CampaignOpportunity;
  onApplied: (next: CampaignOpportunity) => void;
}) {
  const actions = opportunityActionState(opportunity.viewer);
  const nextAction = resolveOpportunityNextAction(actions);
  const NextIcon = nextActionIcon(actions.kind);
  const suggestedMessage = defaultApplicationMessage({
    campaignName: opportunity.name,
    brandName: opportunity.brand?.company_name,
  });
  const [message, setMessage] = useState(suggestedMessage);
  const [busyKey, setBusyKey] = useState<string | null>(null);
  const [confirm, setConfirm] = useState<"accept" | "decline" | null>(null);
  const inviteMessage = opportunity.viewer.invitation?.message?.trim() || null;

  async function refresh() {
    const next = await opportunityApi.get(opportunity.id);
    onApplied(next);
  }

  async function runAction(key: string, action: () => Promise<void>, success: string) {
    setBusyKey(key);
    try {
      await action();
      toast.success(success);
      await refresh();
    } catch (err) {
      toast.error(err instanceof ApiError ? err.message : "Something went wrong.");
    } finally {
      setBusyKey(null);
      setConfirm(null);
    }
  }

  async function handleApply(event: FormEvent) {
    event.preventDefault();
    if (!actions.canApply) return;
    await runAction(
      "apply",
      async () => {
        await applicationApi.create(opportunity.id, { message: message.trim() || null });
      },
      "Application submitted.",
    );
    setMessage(suggestedMessage);
  }

  return (
    <section
      className="rounded-pv border border-primary/15 bg-primary/[0.03] px-3.5 py-3.5"
      aria-labelledby="opportunity-next-action-title"
    >
      <div className="flex items-start gap-3">
        <span
          className="mt-0.5 inline-flex size-8 shrink-0 items-center justify-center rounded-pv bg-primary/5 text-primary"
          aria-hidden="true"
        >
          <NextIcon className="size-4" />
        </span>
        <div className="min-w-0">
          <p className="text-[0.6875rem] font-medium uppercase tracking-[0.06em] text-ink-40">
            Next step
          </p>
          <h2
            id="opportunity-next-action-title"
            className="mt-1 text-[0.9375rem] font-semibold tracking-[-0.01em] text-primary"
          >
            {nextAction.title}
          </h2>
          <p className="mt-1 max-w-[42ch] text-[0.8125rem] leading-snug text-ink-60">
            {nextAction.description}
          </p>
        </div>
      </div>

      <div className="mt-3 flex flex-wrap items-center gap-2 pl-11">
        <StatusBadge
          variant={
            actions.kind === "participating" || actions.kind === "accepted"
              ? "success"
              : actions.kind === "application_rejected"
                ? "error"
                : actions.kind === "invitation_pending" || actions.kind === "application_pending"
                  ? "warning"
                  : "neutral"
          }
        >
          {actions.primaryLabel}
        </StatusBadge>
        {opportunity.viewer.application ? (
          <StatusBadge variant={applicationStatusBadgeVariant(opportunity.viewer.application.status)}>
            {applicationStatusLabel(opportunity.viewer.application.status)}
          </StatusBadge>
        ) : null}
        {opportunity.viewer.invitation ? (
          <StatusBadge variant={invitationStatusBadgeVariant(opportunity.viewer.invitation.status)}>
            {invitationStatusLabel(opportunity.viewer.invitation.status)}
          </StatusBadge>
        ) : null}
      </div>

      {inviteMessage && actions.kind === "invitation_pending" ? (
        <blockquote className="mt-3 border-l-2 border-line pl-3 text-[0.8125rem] leading-snug text-ink-60 sm:ml-11">
          <span className="text-ink-40">From the brand: </span>
          {inviteMessage}
        </blockquote>
      ) : null}

      <div className="mt-3 space-y-3 pl-11">
        {actions.kind === "participating" && actions.participantId ? (
          <Button size="sm" href={participantWorkspacePath(actions.participantId)}>
            <ArrowTopRightOnSquareIcon className="size-4" aria-hidden="true" />
            <span className="ml-1.5">Open campaign workspace</span>
          </Button>
        ) : null}

        {actions.canAcceptInvitation && actions.invitationId ? (
          <div className="flex flex-wrap gap-2">
            <Button size="sm" disabled={busyKey !== null} onClick={() => setConfirm("accept")}>
              <CheckIcon className="size-4" aria-hidden="true" />
              <span className="ml-1.5">Accept invitation</span>
            </Button>
            <Button
              size="sm"
              variant="secondary"
              disabled={busyKey !== null}
              onClick={() => setConfirm("decline")}
            >
              <XMarkIcon className="size-4" aria-hidden="true" />
              <span className="ml-1.5">Reject invitation</span>
            </Button>
          </div>
        ) : null}

        {actions.canWithdrawApplication && actions.applicationId ? (
          <Button
            size="sm"
            variant="secondary"
            disabled={busyKey !== null}
            onClick={() =>
              void runAction(
                "withdraw",
                async () => {
                  await applicationApi.withdraw(actions.applicationId as number);
                },
                "Application withdrawn.",
              )
            }
          >
            <XMarkIcon className="size-4" aria-hidden="true" />
            <span className="ml-1.5">
              {busyKey === "withdraw" ? "Withdrawing…" : "Withdraw application"}
            </span>
          </Button>
        ) : null}

        {actions.canApply ? (
          <form onSubmit={(event) => void handleApply(event)} className="space-y-2">
            <label className="block">
              <span className="mb-1 block text-[0.6875rem] font-medium uppercase tracking-[0.06em] text-ink-40">
                Message (optional)
              </span>
              <textarea
                className={textareaClassName}
                rows={4}
                maxLength={2000}
                value={message}
                onChange={(event) => setMessage(event.target.value)}
              />
              <span className="mt-1 block text-[0.75rem] text-ink-40">
                Suggested message — edit or clear before sending.
              </span>
            </label>
            <Button type="submit" size="sm" disabled={busyKey !== null}>
              <PaperAirplaneIcon className="size-4" aria-hidden="true" />
              <span className="ml-1.5">
                {busyKey === "apply" ? "Applying…" : actions.applyLabel}
              </span>
            </Button>
          </form>
        ) : null}
      </div>

      {confirm === "accept" && actions.invitationId ? (
        <ConfirmDialog
          title="Accept this invitation?"
          description="You’ll join the campaign as a participant and can start assigned deliverables."
          confirmLabel="Accept invitation"
          busy={busyKey === "accept"}
          busyLabel="Accepting…"
          onCancel={() => setConfirm(null)}
          onConfirm={() =>
            void runAction(
              "accept",
              async () => {
                await invitationApi.accept(actions.invitationId as number);
              },
              "Invitation accepted.",
            )
          }
        />
      ) : null}

      {confirm === "decline" && actions.invitationId ? (
        <ConfirmDialog
          title="Reject this invitation?"
          description="The brand will be notified. You will not join this campaign."
          confirmLabel="Reject invitation"
          busy={busyKey === "decline"}
          busyLabel="Rejecting…"
          onCancel={() => setConfirm(null)}
          onConfirm={() =>
            void runAction(
              "decline",
              async () => {
                await invitationApi.decline(actions.invitationId as number);
              },
              "Invitation rejected.",
            )
          }
        />
      ) : null}
    </section>
  );
}

function OpportunityDetailHeader({
  opportunity,
  primaryAction,
}: {
  opportunity: CampaignOpportunity;
  primaryAction: { label: string; href: string } | null;
}) {
  const description = opportunity.description?.trim() || null;
  const networks =
    opportunity.platforms.length > 0
      ? opportunity.platforms.map((platform) => socialProviderLabel(platform)).join(", ")
      : null;

  return (
    <header className="space-y-4">
      <div>
        <Button href="/dashboard/opportunities" size="sm" variant="secondary">
          <ArrowLeftIcon className="size-4" aria-hidden="true" />
          <span className="ml-1.5">Back to Opportunities</span>
        </Button>
      </div>

      <div className="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
        <div className="min-w-0 space-y-2">
          <div className="flex flex-wrap items-center gap-2">
            <h1 className="text-[1.375rem] font-semibold tracking-[-0.02em] text-primary sm:text-[1.5rem]">
              {opportunity.name}
            </h1>
            <StatusBadge variant={campaignStatusBadgeVariant(opportunity.status)}>
              {campaignStatusLabel(opportunity.status)}
            </StatusBadge>
          </div>

          {description ? (
            <p className="max-w-[62ch] whitespace-pre-wrap text-[0.875rem] leading-relaxed text-ink-60">
              {description}
            </p>
          ) : null}

          <div className="flex flex-wrap gap-x-4 gap-y-2">
            <FactChip icon={BuildingStorefrontIcon} label={brandLabel(opportunity)} />
            <FactChip
              icon={CalendarDaysIcon}
              label={formatDateRange(opportunity.start_date, opportunity.end_date)}
            />
            <FactChip
              icon={BanknotesIcon}
              label={formatMoneyMinor(opportunity.budget_amount, opportunity.currency)}
            />
            {networks ? <FactChip icon={GlobeAltIcon} label={networks} /> : null}
          </div>
        </div>

        {primaryAction ? (
          <div className="flex shrink-0 flex-wrap items-center gap-2 lg:justify-end">
            <Button size="sm" href={primaryAction.href}>
              <ArrowTopRightOnSquareIcon className="size-4" aria-hidden="true" />
              <span className="ml-1.5">{primaryAction.label}</span>
            </Button>
          </div>
        ) : null}
      </div>
    </header>
  );
}

function CreatorOpportunityDetailContent({ campaignId }: { campaignId: number }) {
  const [opportunity, setOpportunity] = useState<CampaignOpportunity | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  const load = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      setOpportunity(await opportunityApi.get(campaignId));
    } catch (err) {
      setError(err instanceof ApiError ? err.message : "Unable to load this campaign.");
      setOpportunity(null);
    } finally {
      setIsLoading(false);
    }
  }, [campaignId]);

  useEffect(() => {
    void load();
  }, [load]);

  if (isLoading) {
    return (
      <DashboardPageContainer>
        <CampaignDetailSkeleton />
      </DashboardPageContainer>
    );
  }

  if (error || !opportunity) {
    return (
      <DashboardPageContainer>
        <ErrorState
          title="Couldn’t load campaign"
          message={error ?? "This campaign is not available."}
          action={
            <Button href="/dashboard/opportunities" size="sm" variant="secondary">
              <ArrowLeftIcon className="size-4" aria-hidden="true" />
              <span className="ml-1.5">Back to Opportunities</span>
            </Button>
          }
          retryLabel="Retry"
          onRetry={() => void load()}
        />
      </DashboardPageContainer>
    );
  }

  const actions = opportunityActionState(opportunity.viewer);
  const audience = buildCampaignAudienceView(opportunity);
  const deliverableCount = campaignDeliverableUnitCount(opportunity.deliverables);
  const headerAction =
    actions.kind === "participating" && actions.participantId
      ? { label: "Open workspace", href: participantWorkspacePath(actions.participantId) }
      : null;

  return (
    <DashboardPageContainer>
      <OpportunityDetailHeader opportunity={opportunity} primaryAction={headerAction} />

      <div className="mt-5 space-y-5">
        <OpportunityParticipatePanel opportunity={opportunity} onApplied={setOpportunity} />

        <div className="grid gap-6 xl:grid-cols-[minmax(0,1fr)_16.5rem] xl:items-start">
          <div className="min-w-0 space-y-5">
            <div className="space-y-8 rounded-pv border border-line bg-surface px-4 py-4 sm:px-5">
              <CampaignAudienceSection audience={audience} />
              <CampaignBriefDeliverables
                deliverables={opportunity.deliverables}
                title="What you’ll post"
                description={
                  deliverableCount > 0
                    ? `${deliverableCount} deliverable${deliverableCount === 1 ? "" : "s"} in this brief.`
                    : "The posts and stories expected for this campaign."
                }
                emptyTitle="No deliverables listed"
                emptyDescription="The brand has not specified brief items yet."
              />
            </div>
          </div>

          <aside className="space-y-5 xl:sticky xl:top-4">
            <CampaignScheduleCard campaign={opportunity} />
            <OpportunityCompensationPanel opportunity={opportunity} />
          </aside>
        </div>
      </div>
    </DashboardPageContainer>
  );
}

export function CreatorOpportunityDetailPage({ campaignId }: { campaignId: number }) {
  return (
    <DashboardShell title="Campaign opportunity" allowedAccountTypes={CREATOR_ONLY}>
      {() => <CreatorOpportunityDetailContent campaignId={campaignId} />}
    </DashboardShell>
  );
}
