"use client";

import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import { toast } from "sonner";
import { Button } from "@/components/Button";
import { SocialProviderIcon } from "@/components/dashboard/SocialProviderIcon";
import { DashboardPageContainer } from "@/components/dashboard/DashboardPageContainer";
import { DashboardShell } from "@/components/dashboard/DashboardShell";
import { Card } from "@/components/ui/Card";
import { EmptyState } from "@/components/ui/EmptyState";
import { ErrorState } from "@/components/ui/ErrorState";
import { ConfirmDialog } from "@/components/ui/ConfirmDialog";
import { PageHeader } from "@/components/ui/PageHeader";
import { Skeleton } from "@/components/ui/Skeleton";
import { StatusBadge } from "@/components/ui/StatusBadge";
import { ApiError } from "@/lib/api";
import {
  applicationApi,
  invitationApi,
  opportunityApi,
  participantApi,
} from "@/lib/api/campaign-participation";
import {
  APPLICATION_FILTERS,
  applicationFilterToQuery,
  joinedTabNeedsAttention,
  opportunityActionState,
  opportunityDetailPath,
  parseOpportunityTabParam,
  participantProgressLabel,
  participantWorkspacePath,
  pendingInvitationCount,
  resolveOpportunityDefaultTab,
  sortParticipantsForWorkQueue,
  type ApplicationFilter,
  type OpportunityTabKey,
} from "@/lib/campaign-opportunity";
import {
  applicationStatusBadgeVariant,
  applicationStatusLabel,
  invitationStatusBadgeVariant,
  invitationStatusLabel,
  participantStatusBadgeVariant,
  participantStatusLabel,
} from "@/lib/campaign-participation";
import {
  campaignAudienceGenderLabel,
  campaignDeliverableLabel,
  campaignStatusBadgeVariant,
  campaignStatusLabel,
} from "@/lib/campaigns";
import { cn } from "@/lib/cn";
import { creatorNicheLabel } from "@/lib/creator-personal-info";
import { formatDateRange, formatMoneyMinor, formatRelativeTime } from "@/lib/format";
import { participantSourceLabel } from "@/lib/campaign-management";
import { socialProviderLabel } from "@/lib/social-providers";
import type { CampaignListMeta } from "@/types/campaign";
import type {
  CampaignApplication,
  CampaignInvitation,
  CampaignOpportunity,
  CampaignParticipant,
} from "@/types/campaign-participation";

const CREATOR_ONLY = ["creator"] as const;

type TabKey = OpportunityTabKey;

const APPLICATION_FILTER_LABELS: Record<ApplicationFilter, string> = {
  all: "All",
  pending: "Pending",
  accepted: "Accepted",
  rejected: "Rejected",
};

function brandName(brand?: { company_name: string | null; username: string }): string {
  if (!brand) return "Brand";
  return brand.company_name?.trim() || brand.username || "Brand";
}

function ListSkeleton() {
  return (
    <div className="space-y-5" aria-busy="true" aria-live="polite">
      <div className="flex gap-2 border-b border-line pb-px">
        {Array.from({ length: 4 }).map((_, index) => (
          <Skeleton key={index} className="h-9 w-28 rounded-none" />
        ))}
      </div>
      <div className="grid gap-3 sm:grid-cols-2">
        {Array.from({ length: 4 }).map((_, index) => (
          <div key={index} className="rounded-pv border border-line bg-surface p-4">
            <Skeleton className="h-3 w-24" />
            <Skeleton className="mt-2 h-5 w-48" />
            <Skeleton className="mt-3 h-3 w-full" />
            <Skeleton className="mt-2 h-3 w-2/3" />
            <div className="mt-4 flex gap-2">
              <Skeleton className="h-9 w-24" />
              <Skeleton className="h-9 w-20" />
            </div>
          </div>
        ))}
      </div>
    </div>
  );
}

function Pager({
  meta,
  disabled,
  onPageChange,
}: {
  meta: CampaignListMeta | null;
  disabled: boolean;
  onPageChange: (page: number) => void;
}) {
  if (!meta || meta.last_page <= 1) return null;
  return (
    <div className="flex items-center justify-between gap-3 pt-1">
      <p className="text-[0.75rem] text-ink-40">
        Page {meta.current_page} of {meta.last_page}
        <span className="ml-1.5 tabular-nums">· {meta.total} total</span>
      </p>
      <div className="flex gap-2">
        <Button
          size="sm"
          variant="secondary"
          disabled={meta.current_page <= 1 || disabled}
          onClick={() => onPageChange(meta.current_page - 1)}
        >
          Previous
        </Button>
        <Button
          size="sm"
          variant="secondary"
          disabled={meta.current_page >= meta.last_page || disabled}
          onClick={() => onPageChange(meta.current_page + 1)}
        >
          Next
        </Button>
      </div>
    </div>
  );
}

function PlatformRow({ platforms }: { platforms: string[] }) {
  if (platforms.length === 0) return <span className="text-ink-40">—</span>;
  return (
    <span className="inline-flex flex-wrap items-center gap-1.5">
      {platforms.map((platform) => (
        <span
          key={platform}
          className="inline-flex items-center gap-1 rounded border border-line bg-bg px-1.5 py-0.5"
          title={socialProviderLabel(platform)}
        >
          <SocialProviderIcon provider={platform} className="size-3.5" />
          <span className="text-[0.6875rem] font-medium text-ink-60">
            {socialProviderLabel(platform)}
          </span>
        </span>
      ))}
    </span>
  );
}

function MetaChip({ label, value }: { label: string; value: ReactNode }) {
  return (
    <div className="min-w-0">
      <dt className="text-[0.625rem] font-medium uppercase tracking-[0.06em] text-ink-40">{label}</dt>
      <dd className="mt-0.5 truncate text-[0.8125rem] text-primary">{value}</dd>
    </div>
  );
}

function OpportunityCard({
  opportunity,
  busyKey,
  onAcceptInvite,
  onDeclineInvite,
}: {
  opportunity: CampaignOpportunity;
  busyKey: string | null;
  onAcceptInvite: (invitationId: number) => void;
  onDeclineInvite: (invitationId: number) => void;
}) {
  const audience = opportunity.target_audience;
  const genderLabel =
    audience?.genders && audience.genders.length > 0
      ? audience.genders.map((value) => campaignAudienceGenderLabel(value)).join(", ")
      : null;
  const actions = opportunityActionState(opportunity.viewer);
  const href = opportunityDetailPath(opportunity.id);
  const audienceLine = [
    audience?.locations?.map((item) => item.display_name).join(", "),
    genderLabel,
    audience?.interests?.map((item) => creatorNicheLabel(item)).join(", "),
  ]
    .filter(Boolean)
    .join(" · ");
  const isInvited = actions.kind === "invitation_pending";
  const primaryHref =
    actions.kind === "participating" && actions.participantId
      ? participantWorkspacePath(actions.participantId)
      : href;

  return (
    <Card
      elevated={isInvited}
      className={cn(
        "flex flex-col transition-[border-color,box-shadow] duration-150 ease-out",
        isInvited ? "border-accent/35 ring-1 ring-accent/15" : "hover:border-primary/25",
      )}
    >
      <div className="flex flex-wrap items-start justify-between gap-2">
        <div className="min-w-0">
          <p className="text-[0.75rem] font-medium text-ink-60">{brandName(opportunity.brand)}</p>
          <h3 className="mt-0.5 text-[1rem] font-semibold tracking-[-0.015em] text-primary">
            <Link
              href={href}
              className="transition-colors duration-150 ease-out hover:text-accent focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent"
            >
              {opportunity.name}
            </Link>
          </h3>
        </div>
        <div className="flex flex-wrap items-center gap-1.5">
          <StatusBadge variant={campaignStatusBadgeVariant(opportunity.status)}>
            {campaignStatusLabel(opportunity.status)}
          </StatusBadge>
          {actions.kind !== "apply" ? (
            <StatusBadge
              variant={
                actions.kind === "participating" || actions.kind === "accepted"
                  ? "success"
                  : actions.kind === "application_rejected"
                    ? "error"
                    : actions.kind === "invitation_pending"
                      ? "accent"
                      : "warning"
              }
              pulse={actions.kind === "invitation_pending"}
            >
              {actions.primaryLabel}
            </StatusBadge>
          ) : null}
        </div>
      </div>

      <p className="mt-2 line-clamp-2 text-[0.8125rem] leading-snug text-ink-60">
        {opportunity.description?.trim() || "No description provided."}
      </p>

      <dl className="mt-3 grid grid-cols-2 gap-x-3 gap-y-2.5 border-t border-line pt-3 sm:grid-cols-4">
        <MetaChip
          label="Dates"
          value={formatDateRange(opportunity.start_date, opportunity.end_date)}
        />
        <MetaChip
          label="Budget"
          value={formatMoneyMinor(opportunity.budget_amount, opportunity.currency)}
        />
        <div className="col-span-2 min-w-0 sm:col-span-2">
          <dt className="text-[0.625rem] font-medium uppercase tracking-[0.06em] text-ink-40">
            Networks
          </dt>
          <dd className="mt-0.5">
            <PlatformRow platforms={opportunity.platforms} />
          </dd>
        </div>
      </dl>

      {audienceLine ? (
        <p className="mt-2.5 line-clamp-1 text-[0.75rem] text-ink-40">
          <span className="font-medium text-ink-60">Audience</span>
          <span className="mx-1.5 text-line">·</span>
          {audienceLine}
        </p>
      ) : null}

      {opportunity.deliverables.length > 0 ? (
        <ul className="mt-2.5 flex flex-wrap gap-1.5">
          {opportunity.deliverables.slice(0, 4).map((item, index) => (
            <li
              key={`${item.type}-${index}`}
              className="rounded border border-line bg-bg px-1.5 py-0.5 text-[0.6875rem] font-medium text-ink-60"
            >
              {campaignDeliverableLabel(item.type)} ×{item.quantity}
            </li>
          ))}
          {opportunity.deliverables.length > 4 ? (
            <li className="rounded border border-dashed border-line px-1.5 py-0.5 text-[0.6875rem] text-ink-40">
              +{opportunity.deliverables.length - 4} more
            </li>
          ) : null}
        </ul>
      ) : null}

      <div className="mt-auto flex flex-wrap items-center gap-2 border-t border-line pt-3">
        {actions.canAcceptInvitation && actions.invitationId ? (
          <>
            <Button
              size="sm"
              disabled={busyKey === `accept-${actions.invitationId}`}
              onClick={() => onAcceptInvite(actions.invitationId as number)}
            >
              {busyKey === `accept-${actions.invitationId}` ? "Accepting…" : "Accept invitation"}
            </Button>
            <Button
              size="sm"
              variant="secondary"
              disabled={busyKey === `decline-${actions.invitationId}`}
              onClick={() => onDeclineInvite(actions.invitationId as number)}
            >
              {busyKey === `decline-${actions.invitationId}` ? "Rejecting…" : "Reject"}
            </Button>
            <Button size="sm" variant="secondary" href={href}>
              View brief
            </Button>
          </>
        ) : actions.kind === "participating" ? (
          <>
            <Button size="sm" href={primaryHref}>
              Open workspace
            </Button>
            <Button size="sm" variant="secondary" href={href}>
              View brief
            </Button>
          </>
        ) : actions.canApply ? (
          <Button size="sm" href={href}>
            {actions.applyLabel}
          </Button>
        ) : (
          <Button size="sm" href={href}>
            View brief
          </Button>
        )}
      </div>
    </Card>
  );
}

function InvitationRow({
  invitation,
  busyKey,
  onAccept,
  onDecline,
}: {
  invitation: CampaignInvitation;
  busyKey: string | null;
  onAccept: (id: number) => void;
  onDecline: (id: number) => void;
}) {
  const pending = invitation.status === "pending";

  return (
    <Card
      padding="compact"
      elevated={pending}
      className={cn(
        "flex flex-col gap-3 transition-[border-color] duration-150 ease-out sm:flex-row sm:items-start sm:justify-between",
        pending ? "border-accent/35" : "hover:border-primary/25",
      )}
    >
      <div className="min-w-0 space-y-1.5">
        <div className="flex flex-wrap items-center gap-2">
          <Link
            href={opportunityDetailPath(invitation.campaign.id)}
            className="text-[0.9375rem] font-semibold tracking-[-0.01em] text-primary transition-colors duration-150 ease-out hover:text-accent"
          >
            {invitation.campaign.name}
          </Link>
          <StatusBadge variant={invitationStatusBadgeVariant(invitation.status)} pulse={pending}>
            {invitationStatusLabel(invitation.status)}
          </StatusBadge>
        </div>
        <p className="text-[0.8125rem] text-ink-60">
          {brandName(invitation.campaign.brand)}
          <span className="mx-1.5 text-ink-40">·</span>
          Received {formatRelativeTime(invitation.created_at)}
        </p>
        <p className="text-[0.75rem] text-ink-40">
          {formatDateRange(invitation.campaign.start_date, invitation.campaign.end_date)}
        </p>
        {invitation.message ? (
          <p className="rounded border border-line bg-bg px-2.5 py-2 text-[0.8125rem] leading-snug text-ink-60">
            {invitation.message}
          </p>
        ) : null}
      </div>
      <div className="flex shrink-0 flex-wrap gap-2">
        {pending ? (
          <>
            <Button size="sm" disabled={busyKey !== null} onClick={() => onAccept(invitation.id)}>
              Accept
            </Button>
            <Button
              size="sm"
              variant="secondary"
              disabled={busyKey !== null}
              onClick={() => onDecline(invitation.id)}
            >
              Reject
            </Button>
          </>
        ) : null}
        <Button size="sm" variant="secondary" href={opportunityDetailPath(invitation.campaign.id)}>
          View brief
        </Button>
      </div>
    </Card>
  );
}

function ApplicationRow({
  application,
  busyKey,
  onWithdraw,
}: {
  application: CampaignApplication;
  busyKey: string | null;
  onWithdraw: (id: number) => void;
}) {
  return (
    <Card
      padding="compact"
      className="flex flex-col gap-3 transition-[border-color] duration-150 ease-out hover:border-primary/25 sm:flex-row sm:items-center sm:justify-between"
    >
      <div className="min-w-0 space-y-1.5">
        <div className="flex flex-wrap items-center gap-2">
          <Link
            href={opportunityDetailPath(application.campaign.id)}
            className="text-[0.9375rem] font-semibold tracking-[-0.01em] text-primary transition-colors duration-150 ease-out hover:text-accent"
          >
            {application.campaign.name}
          </Link>
          <StatusBadge variant={applicationStatusBadgeVariant(application.status)}>
            {applicationStatusLabel(application.status)}
          </StatusBadge>
        </div>
        <p className="text-[0.8125rem] text-ink-60">
          {brandName(application.campaign.brand)}
          <span className="mx-1.5 text-ink-40">·</span>
          Submitted {formatRelativeTime(application.created_at)}
        </p>
        <div className="flex flex-wrap items-center gap-2 text-[0.75rem] text-ink-40">
          <PlatformRow platforms={application.campaign.platforms} />
          <span>
            {formatDateRange(application.campaign.start_date, application.campaign.end_date)}
          </span>
        </div>
      </div>
      <div className="flex shrink-0 flex-wrap gap-2">
        <Button size="sm" variant="secondary" href={opportunityDetailPath(application.campaign.id)}>
          View brief
        </Button>
        {application.status === "pending" ? (
          <Button
            size="sm"
            variant="secondary"
            disabled={busyKey === `withdraw-${application.id}`}
            onClick={() => onWithdraw(application.id)}
          >
            {busyKey === `withdraw-${application.id}` ? "Withdrawing…" : "Withdraw"}
          </Button>
        ) : null}
      </div>
    </Card>
  );
}

function ParticipationRow({ participant }: { participant: CampaignParticipant }) {
  const progress = participant.deliverable_progress;
  const progressLabel = participantProgressLabel(progress);
  const progressPct =
    progress && progress.total > 0
      ? Math.min(100, Math.round((progress.approved / progress.total) * 100))
      : null;

  return (
    <Card
      padding="compact"
      className="flex flex-col gap-3 transition-[border-color] duration-150 ease-out hover:border-primary/25 sm:flex-row sm:items-center sm:justify-between"
    >
      <div className="min-w-0 space-y-1.5">
        <div className="flex flex-wrap items-center gap-2">
          <Link
            href={
              participant.status === "active"
                ? participantWorkspacePath(participant.id)
                : opportunityDetailPath(participant.campaign.id)
            }
            className="text-[0.9375rem] font-semibold tracking-[-0.01em] text-primary transition-colors duration-150 ease-out hover:text-accent"
          >
            {participant.campaign.name}
          </Link>
          <StatusBadge variant={participantStatusBadgeVariant(participant.status)}>
            {participantStatusLabel(participant.status)}
          </StatusBadge>
        </div>
        <p className="text-[0.8125rem] text-ink-60">
          {brandName(participant.campaign.brand)}
          <span className="mx-1.5 text-ink-40">·</span>
          Via {participantSourceLabel(participant.source)}
        </p>
        <p className="text-[0.75rem] text-ink-40">
          {formatDateRange(participant.campaign.start_date, participant.campaign.end_date)}
        </p>
        {progressLabel && progressPct !== null ? (
          <div className="max-w-xs space-y-1 pt-0.5">
            <div className="flex items-center justify-between gap-2 text-[0.6875rem] text-ink-40">
              <span>{progressLabel}</span>
              <span className="tabular-nums">{progressPct}%</span>
            </div>
            <div className="h-1 overflow-hidden rounded-full bg-line" aria-hidden="true">
              <div
                className="h-full rounded-full bg-accent transition-[width] duration-200 ease-out"
                style={{ width: `${progressPct}%` }}
              />
            </div>
          </div>
        ) : null}
      </div>
      <div className="flex shrink-0 flex-wrap gap-2">
        {participant.status === "active" ? (
          <Button size="sm" href={participantWorkspacePath(participant.id)}>
            Workspace
          </Button>
        ) : null}
        <Button size="sm" variant="secondary" href={opportunityDetailPath(participant.campaign.id)}>
          View brief
        </Button>
      </div>
    </Card>
  );
}

function CreatorOpportunitiesContent() {
  const router = useRouter();
  const searchParams = useSearchParams();
  const tabFromUrl = parseOpportunityTabParam(searchParams.get("tab"));

  const [tab, setTabState] = useState<TabKey>(tabFromUrl ?? "opportunities");
  const [opportunities, setOpportunities] = useState<CampaignOpportunity[]>([]);
  const [invitations, setInvitations] = useState<CampaignInvitation[]>([]);
  const [applications, setApplications] = useState<CampaignApplication[]>([]);
  const [participants, setParticipants] = useState<CampaignParticipant[]>([]);
  const [oppMeta, setOppMeta] = useState<CampaignListMeta | null>(null);
  const [inviteMeta, setInviteMeta] = useState<CampaignListMeta | null>(null);
  const [appMeta, setAppMeta] = useState<CampaignListMeta | null>(null);
  const [partMeta, setPartMeta] = useState<CampaignListMeta | null>(null);
  const [oppPage, setOppPage] = useState(1);
  const [invitePage, setInvitePage] = useState(1);
  const [appPage, setAppPage] = useState(1);
  const [partPage, setPartPage] = useState(1);
  const [appFilter, setAppFilter] = useState<ApplicationFilter>("all");
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [busyKey, setBusyKey] = useState<string | null>(null);
  const [confirmInvite, setConfirmInvite] = useState<{ id: number; action: "accept" | "decline" } | null>(
    null,
  );
  const [didResolveDefaultTab, setDidResolveDefaultTab] = useState(Boolean(tabFromUrl));

  const writeTabToUrl = useCallback(
    (next: TabKey) => {
      const params = new URLSearchParams(searchParams.toString());
      params.set("tab", next);
      router.replace(`?${params.toString()}`, { scroll: false });
    },
    [router, searchParams],
  );

  const setTab = useCallback(
    (next: TabKey) => {
      setTabState(next);
      writeTabToUrl(next);
    },
    [writeTabToUrl],
  );

  const load = useCallback(
    async (silent = false) => {
      if (!silent) setIsLoading(true);
      setError(null);
      try {
        const [opp, invites, apps, parts] = await Promise.all([
          opportunityApi.list({ page: oppPage, per_page: 12 }),
          invitationApi.listMine({ page: invitePage, per_page: 20 }),
          applicationApi.listMine({
            page: appPage,
            per_page: 20,
            status: applicationFilterToQuery(appFilter),
          }),
          participantApi.listMine({ page: partPage, per_page: 20 }),
        ]);
        setOpportunities(opp.opportunities);
        setOppMeta(opp.meta);
        setInvitations(invites.invitations);
        setInviteMeta(invites.meta);
        setApplications(apps.applications);
        setAppMeta(apps.meta);
        setParticipants(parts.participants);
        setPartMeta(parts.meta);
      } catch (err) {
        setError(err instanceof ApiError ? err.message : "Unable to load opportunities.");
      } finally {
        setIsLoading(false);
      }
    },
    [oppPage, invitePage, appPage, partPage, appFilter],
  );

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

  useEffect(() => {
    if (!tabFromUrl) {
      return;
    }
    setTabState(tabFromUrl);
    setDidResolveDefaultTab(true);
  }, [tabFromUrl]);

  const pendingInviteCount = useMemo(
    () => pendingInvitationCount(invitations),
    [invitations],
  );

  const joinedNeedsAttention = useMemo(
    () => joinedTabNeedsAttention(participants),
    [participants],
  );

  const sortedParticipants = useMemo(
    () => sortParticipantsForWorkQueue(participants),
    [participants],
  );

  useEffect(() => {
    if (didResolveDefaultTab || isLoading || error) {
      return;
    }
    if (tabFromUrl) {
      setDidResolveDefaultTab(true);
      return;
    }

    const next = resolveOpportunityDefaultTab({
      pendingInvitationCount: pendingInviteCount,
      participants,
    });
    setTabState(next);
    writeTabToUrl(next);
    setDidResolveDefaultTab(true);
  }, [
    didResolveDefaultTab,
    error,
    isLoading,
    participants,
    pendingInviteCount,
    tabFromUrl,
    writeTabToUrl,
  ]);

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

  const tabs: { key: TabKey; label: string; count: number; highlight?: boolean }[] = [
    { key: "opportunities", label: "Open", count: oppMeta?.total ?? 0 },
    {
      key: "invitations",
      label: "Invitations",
      count: pendingInviteCount,
      highlight: pendingInviteCount > 0,
    },
    { key: "applications", label: "Applications", count: appMeta?.total ?? 0 },
    {
      key: "participations",
      label: "Joined",
      count: partMeta?.total ?? 0,
      highlight: joinedNeedsAttention,
    },
  ];

  return (
    <DashboardPageContainer>
      <PageHeader description="Browse live campaigns, respond to invitations, and track the work you’ve joined." />

      {isLoading ? <ListSkeleton /> : null}

      {!isLoading && error ? (
        <ErrorState
          title="Couldn’t load opportunities"
          message={error}
          retryLabel="Retry"
          onRetry={() => void load()}
        />
      ) : null}

      {!isLoading && !error ? (
        <>
          {pendingInviteCount > 0 && tab !== "invitations" ? (
            <Card
              padding="compact"
              className="flex flex-col gap-3 border-accent/30 bg-accent/[0.04] sm:flex-row sm:items-center sm:justify-between"
            >
              <div className="min-w-0">
                <p className="text-[0.8125rem] font-semibold text-primary">
                  {pendingInviteCount === 1
                    ? "You have 1 pending invitation"
                    : `You have ${pendingInviteCount} pending invitations`}
                </p>
                <p className="mt-0.5 text-[0.75rem] text-ink-60">
                  Brands are waiting on your response. Accept or reject to keep moving.
                </p>
              </div>
              <Button size="sm" onClick={() => setTab("invitations")}>
                Review invitations
              </Button>
            </Card>
          ) : null}

          <div
            className="-mx-1 flex gap-1 overflow-x-auto border-b border-line px-1"
            role="tablist"
            aria-label="Opportunity sections"
          >
            {tabs.map((item) => {
              const selected = tab === item.key;
              return (
                <button
                  key={item.key}
                  type="button"
                  role="tab"
                  aria-selected={selected}
                  className={cn(
                    "relative shrink-0 px-3 py-2.5 text-[0.8125rem] font-medium transition-colors duration-150 ease-out",
                    "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
                    selected ? "text-primary" : "text-ink-60 hover:text-primary",
                  )}
                  onClick={() => setTab(item.key)}
                >
                  <span className="inline-flex items-center gap-1.5">
                    {item.label}
                    <span
                      className={cn(
                        "inline-flex min-w-5 items-center justify-center rounded px-1 py-0.5 text-[0.6875rem] font-semibold tabular-nums",
                        selected
                          ? "bg-primary text-white"
                          : item.highlight
                            ? "bg-accent/15 text-accent"
                            : "bg-line/60 text-ink-60",
                      )}
                    >
                      {item.count}
                    </span>
                  </span>
                  <span
                    aria-hidden="true"
                    className={cn(
                      "absolute inset-x-2 bottom-0 h-0.5 rounded-full transition-[background-color,transform] duration-150 ease-out",
                      selected ? "scale-x-100 bg-accent" : "scale-x-75 bg-transparent",
                    )}
                  />
                </button>
              );
            })}
          </div>

          {tab === "opportunities" ? (
            <section className="space-y-3" aria-label="Open campaigns">
              {opportunities.length === 0 ? (
                <EmptyState
                  title="No open campaigns right now"
                  description="When brands publish active campaigns, they’ll show up here for you to apply."
                  action={
                    participants.length > 0 || invitations.length > 0 ? (
                      <div className="flex flex-wrap justify-center gap-2">
                        {participants.length > 0 ? (
                          <Button size="sm" variant="secondary" onClick={() => setTab("participations")}>
                            View joined campaigns
                          </Button>
                        ) : null}
                        {invitations.length > 0 ? (
                          <Button size="sm" variant="secondary" onClick={() => setTab("invitations")}>
                            {pendingInviteCount > 0 ? "Review invitations" : "View invitations"}
                          </Button>
                        ) : null}
                      </div>
                    ) : undefined
                  }
                />
              ) : (
                <div className="grid gap-3 lg:grid-cols-2">
                  {opportunities.map((opportunity) => (
                    <OpportunityCard
                      key={opportunity.id}
                      opportunity={opportunity}
                      busyKey={busyKey}
                      onAcceptInvite={(invitationId) =>
                        setConfirmInvite({ id: invitationId, action: "accept" })
                      }
                      onDeclineInvite={(invitationId) =>
                        setConfirmInvite({ id: invitationId, action: "decline" })
                      }
                    />
                  ))}
                </div>
              )}
              <Pager meta={oppMeta} disabled={busyKey !== null} onPageChange={setOppPage} />
            </section>
          ) : null}

          {tab === "invitations" ? (
            <section className="space-y-3" aria-label="Invitations">
              {invitations.length === 0 ? (
                <EmptyState
                  title="No invitations yet"
                  description="When a brand invites you to a campaign, it will appear here with Accept and Reject actions."
                  action={
                    <Button size="sm" variant="secondary" onClick={() => setTab("opportunities")}>
                      Browse open campaigns
                    </Button>
                  }
                />
              ) : (
                <ul className="space-y-2">
                  {invitations.map((invitation) => (
                    <li key={invitation.id}>
                      <InvitationRow
                        invitation={invitation}
                        busyKey={busyKey}
                        onAccept={(id) => setConfirmInvite({ id, action: "accept" })}
                        onDecline={(id) => setConfirmInvite({ id, action: "decline" })}
                      />
                    </li>
                  ))}
                </ul>
              )}
              <Pager meta={inviteMeta} disabled={busyKey !== null} onPageChange={setInvitePage} />
            </section>
          ) : null}

          {tab === "applications" ? (
            <section className="space-y-3" aria-label="My applications">
              <div
                className="inline-flex flex-wrap gap-1 rounded-pv border border-line bg-surface p-1"
                role="group"
                aria-label="Filter applications"
              >
                {APPLICATION_FILTERS.map((filter) => (
                  <button
                    key={filter}
                    type="button"
                    className={cn(
                      "rounded px-2.5 py-1.5 text-[0.75rem] font-medium transition-colors duration-150 ease-out",
                      "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent",
                      appFilter === filter
                        ? "bg-primary text-white"
                        : "text-ink-60 hover:bg-line/50 hover:text-primary",
                    )}
                    onClick={() => {
                      setAppFilter(filter);
                      setAppPage(1);
                    }}
                  >
                    {APPLICATION_FILTER_LABELS[filter]}
                  </button>
                ))}
              </div>
              {applications.length === 0 ? (
                <EmptyState
                  title={appFilter === "all" ? "No applications yet" : `No ${appFilter} applications`}
                  description={
                    appFilter === "all"
                      ? "Apply to an open campaign and track its status here."
                      : "Try another filter, or browse open campaigns to apply."
                  }
                  action={
                    <Button size="sm" variant="secondary" onClick={() => setTab("opportunities")}>
                      Browse open campaigns
                    </Button>
                  }
                />
              ) : (
                <ul className="space-y-2">
                  {applications.map((application) => (
                    <li key={application.id}>
                      <ApplicationRow
                        application={application}
                        busyKey={busyKey}
                        onWithdraw={(id) =>
                          void runAction(
                            `withdraw-${id}`,
                            async () => {
                              await applicationApi.withdraw(id);
                            },
                            "Application withdrawn.",
                          )
                        }
                      />
                    </li>
                  ))}
                </ul>
              )}
              <Pager meta={appMeta} disabled={busyKey !== null} onPageChange={setAppPage} />
            </section>
          ) : null}

          {tab === "participations" ? (
            <section className="space-y-3" aria-label="Joined campaigns">
              {sortedParticipants.length === 0 ? (
                <EmptyState
                  title="No joined campaigns"
                  description="Accepted invitations and applications will show up here with workspace access."
                  action={
                    <Button size="sm" variant="secondary" onClick={() => setTab("opportunities")}>
                      Browse open campaigns
                    </Button>
                  }
                />
              ) : (
                <ul className="space-y-2">
                  {sortedParticipants.map((participant) => (
                    <li key={participant.id}>
                      <ParticipationRow participant={participant} />
                    </li>
                  ))}
                </ul>
              )}
              <Pager meta={partMeta} disabled={busyKey !== null} onPageChange={setPartPage} />
            </section>
          ) : null}
        </>
      ) : null}

      {confirmInvite ? (
        <ConfirmDialog
          title={confirmInvite.action === "accept" ? "Accept this invitation?" : "Reject this invitation?"}
          description={
            confirmInvite.action === "accept"
              ? "You’ll join the campaign as a participant and can start assigned deliverables."
              : "The brand will be notified. You will not join this campaign."
          }
          confirmLabel={confirmInvite.action === "accept" ? "Accept invitation" : "Reject invitation"}
          busy={busyKey !== null}
          busyLabel={confirmInvite.action === "accept" ? "Accepting…" : "Rejecting…"}
          onCancel={() => setConfirmInvite(null)}
          onConfirm={() =>
            void runAction(
              `${confirmInvite.action}-${confirmInvite.id}`,
              async () => {
                if (confirmInvite.action === "accept") {
                  await invitationApi.accept(confirmInvite.id);
                } else {
                  await invitationApi.decline(confirmInvite.id);
                }
              },
              confirmInvite.action === "accept" ? "Invitation accepted." : "Invitation rejected.",
            )
          }
        />
      ) : null}
    </DashboardPageContainer>
  );
}

export function CreatorOpportunitiesPage() {
  return (
    <DashboardShell title="Opportunities" allowedAccountTypes={CREATOR_ONLY}>
      {() => <CreatorOpportunitiesContent />}
    </DashboardShell>
  );
}
