import {
  ArrowRight,
  Layers,
  Link2,
  Lock,
  Play,
  RefreshCw,
  Sparkles,
  SplitSquareHorizontal,
  Target,
} from 'lucide-react';

import { useEffect } from 'react';

import { Head, Link } from '@inertiajs/react';

import { HubDay0Empty } from '@/Components/Hubs/HubDay0Empty';
import EditorialPageHeader from '@/Components/layout/EditorialPageHeader';
import { Button } from '@/Components/ui/button';
import { InfoTooltip } from '@/Components/ui/info-tooltip';
import { SecondaryPanelToggle } from '@/Components/ui/SecondaryPanelToggle';
import DashboardLayout from '@/Layouts/DashboardLayout';
import { trackProductEvent } from '@/lib/analytics';
import { OPPORTUNITIES_HUB_VIEWED } from '@/lib/event-catalog';
import { cn } from '@/lib/utils';
import type { SiteBasic } from '@/types';
import type { InFlightBatch } from '@/types/dashboard';

import { CONTENT_CELLS } from './content-hub-cells';

/**
 * RecoverHub — R6UXS-004
 *
 * The merged "Recover" hub replaces the separate Opportunities + Content hubs,
 * ordering cells by the recovery lifecycle: Decaying → Inventory → Recommend.
 *
 * Route: sites.opportunities.index (OpportunitiesHubController renders this page).
 *
 * The Content hub (sites.content.index → Sites/ContentHub.tsx) remains for
 * backward-compatibility with existing PHP tests; the navigation now surfaces
 * this page as the primary "Recover" entry.
 *
 * R6UXS-010: cells show em-dash / locked when `has_analysis` is false so
 * the user understands the count reflects "not yet run", not "zero".
 *
 * DUX-EOU-02: primary cells (Action queue + Recommendations) are promoted
 * to the first-visible grid; deeper-analysis cells are collapsed behind a
 * SecondaryPanelToggle disclosure. The single "Start Recovery Run" header
 * CTA is preserved as the primary action.
 */

interface RecoverCounts {
  // From OpportunitiesHubController
  pending_recommendations: number;
  keyword_opportunities: number;
  topic_clusters: number;
  cannibalization_cases: number;
  link_opportunities: number;
  draftable_recommendations_count: number;
  /** R8UXB-004: pending FreshnessRecommendation rows for the site. */
  freshness_recommendations: number;
  // From ContentHubController (added when R6UXS-004 merges the two controllers)
  inventory?: number;
  briefs?: number;
  drafts_in_flight?: number;
}

interface Props {
  site: SiteBasic & { has_gsc: boolean; has_analysis: boolean };
  counts?: RecoverCounts;
  /**
   * R16-PD-02: site-scoped count of drafts awaiting review (from OpportunitiesHubController).
   * Overrides the cross-site shared prop on this page.
   */
  pending_review_count?: number;
  /**
   * R16-PD-02: active batch summary or null. When present, the header CTA
   * switches to "Resume — N drafts awaiting review" as the primary action.
   */
  in_flight_batch?: InFlightBatch | null;
}

/** R7UXB-005: Helper to look up a shared Content cell by key. */
function contentCell(key: string) {
  const def = CONTENT_CELLS.find((c) => c.key === key);
  if (!def) throw new Error(`content-hub-cells: unknown key "${key}"`);
  return def;
}

interface HubCell {
  key: string;
  title: string;
  ctaLabel: string;
  description: string;
  routeName: string;
  icon: typeof Sparkles;
  /** Returns count, or null when no meaningful count. */
  count: (c: RecoverCounts) => number | null;
  /** Returns true when the cell is gated (no analysis). Accepts site directly to match CONTENT_CELLS signature. */
  gatedBy?: (site: SiteBasic & { has_analysis: boolean }) => boolean;
  gatedReason?: string;
}

/**
 * Recovery lifecycle order: find decaying pages → open inventory cohort → dispatch batch rewrite.
 * Opportunities cells first (detect), then action cells (act).
 *
 * R7UXB-005: Content cells (inventory, batch-ai, briefs) are derived from the shared
 * CONTENT_CELLS definition so title/ctaLabel/routeName/icon/gating stay in sync with
 * ContentHub without editing two files.
 */
const cells: HubCell[] = [
  // ── Detect: find what's decaying ─────────────────────────────────────────────
  {
    key: 'action_queue',
    count: (c) => c.draftable_recommendations_count,
    title: 'Action queue',
    ctaLabel: 'Triage queue',
    description: 'Every fix worth doing — ranked by lift.',
    routeName: 'opportunity-map.index',
    icon: Target,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to surface your action queue.',
  },
  {
    key: 'pending_recommendations',
    count: (c) => c.pending_recommendations,
    title: 'Recommendations',
    ctaLabel: 'Review recs',
    description: 'Pages that lost traffic and what to do.',
    routeName: 'recommendations.index',
    icon: Sparkles,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to see recommendations.',
  },
  // R8UXB-004: Freshness and Cannibalization — promised by R6IA-002 IA but missing.
  // Placed in the Detect section (lifecycle order: decay surfaces before action surfaces).
  {
    key: 'freshness',
    count: (c) => c.freshness_recommendations,
    title: 'Freshness',
    ctaLabel: 'Open watchlist',
    description: 'Pages that have gone stale and need a refresh.',
    routeName: 'freshness.index',
    icon: RefreshCw,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to surface freshness decay.',
  },
  {
    key: 'cannibalization',
    count: (c) => c.cannibalization_cases,
    title: 'Cannibalization',
    ctaLabel: 'Review conflicts',
    description: 'Pages competing for the same queries — split or merge them.',
    routeName: 'cannibalization.index',
    icon: SplitSquareHorizontal,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to detect cannibalization.',
  },
  // ── Act: open inventory and dispatch batch ────────────────────────────────────
  {
    ...contentCell('content-inventory'),
    // RecoverHub context: pre-seed by click-loss so the run starts from the most-impactful page.
    description: 'Everything published — sort by click loss to start the run.',
    count: (c) => c.inventory ?? null,
  },
  {
    ...contentCell('batch-ai'),
    count: (c) => c.drafts_in_flight ?? null,
    gatedBy: (site) => !site.has_analysis,
  },
  {
    ...contentCell('content-briefs'),
    count: (c) => c.briefs ?? null,
  },
  // ── Deeper analysis ───────────────────────────────────────────────────────────
  {
    key: 'topic_clusters',
    count: (c) => c.topic_clusters,
    title: 'Topic clusters',
    ctaLabel: 'Audit clusters',
    description: 'Pages competing for the same intent.',
    routeName: 'topic-clusters.index',
    icon: Layers,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to detect topic clusters.',
  },
  {
    key: 'link_opportunities',
    count: (c) => c.link_opportunities,
    title: 'Link opportunities',
    ctaLabel: 'Map links',
    description: 'Internal links your top pages should pass on.',
    routeName: 'sites.link-opportunities.index',
    icon: Link2,
    gatedBy: (site) => !site.has_analysis,
    gatedReason: 'Run an analysis to find link opportunities.',
  },
];

// DUX-EOU-02: primary cell keys — promoted to the always-visible first grid.
const PRIMARY_KEYS = ['action_queue', 'pending_recommendations'] as const;

const ZERO_COUNTS: RecoverCounts = {
  pending_recommendations: 0,
  keyword_opportunities: 0,
  topic_clusters: 0,
  cannibalization_cases: 0,
  link_opportunities: 0,
  draftable_recommendations_count: 0,
  freshness_recommendations: 0,
  inventory: 0,
  briefs: 0,
  drafts_in_flight: 0,
};

/** DUX-EOU-02: Shared per-cell render helper so primary and secondary grids use identical markup. */
function renderCell(
  cell: HubCell,
  site: SiteBasic & { has_analysis: boolean },
  resolvedCounts: RecoverCounts,
) {
  const Icon = cell.icon;
  const gated = cell.gatedBy ? cell.gatedBy(site) : false;
  const count = cell.count(resolvedCounts);

  // R6UXS-010: when has_analysis is false, show em-dash instead of
  // a literal '0' that reads as "empty rather than not-yet-run".
  const displayCount = !site.has_analysis && count === 0 ? null : count;
  const showDash = !site.has_analysis && (count === 0 || count === null);

  const hasSignal = !gated && displayCount !== null && displayCount > 0;
  const description =
    gated && cell.gatedReason ? cell.gatedReason : cell.description;

  const body = (
    <>
      <div className="flex items-start justify-between gap-2">
        <div className="relative">
          <Icon
            className={cn(
              'size-4',
              gated
                ? 'text-muted-foreground/60'
                : hasSignal
                  ? 'text-primary'
                  : 'text-muted-foreground/60',
            )}
            aria-hidden="true"
          />
          {gated && (
            <Lock
              className="absolute -bottom-1.5 -right-1.5 size-3 rounded-full bg-background p-[1px] text-muted-foreground"
              aria-hidden="true"
            />
          )}
        </div>
        {!gated && (
          <span className="text-[10px] font-semibold uppercase tracking-[0.16em] text-primary transition-opacity sm:opacity-0 sm:group-hover:opacity-100">
            {cell.ctaLabel} →
          </span>
        )}
        {gated && (
          <span className="text-[10px] font-semibold uppercase tracking-[0.16em] text-muted-foreground/70">
            Locked
          </span>
        )}
      </div>
      <div>
        {/* R6UXS-010: em-dash when has_analysis=false and count=0/null */}
        {showDash ? (
          <div className="font-data text-3xl font-semibold tabular-nums tracking-tight text-muted-foreground/40">
            —
          </div>
        ) : displayCount !== null ? (
          <div
            className={cn(
              'font-data text-3xl font-semibold tabular-nums tracking-tight',
              gated
                ? 'text-muted-foreground/40'
                : hasSignal
                  ? 'text-foreground'
                  : 'text-muted-foreground/40',
            )}
          >
            {displayCount}
          </div>
        ) : null}
        <p
          className={cn(
            'text-xs font-medium',
            (displayCount !== null || showDash) ? 'mt-1.5' : 'mt-0',
            gated && 'text-muted-foreground',
          )}
        >
          {cell.title}
        </p>
        <p className="text-[11px] text-muted-foreground mt-0.5">{description}</p>
      </div>
    </>
  );

  if (gated) {
    return (
      <span
        key={cell.key}
        aria-disabled="true"
        className="flex flex-col justify-between gap-3 bg-card p-5 min-h-[140px] opacity-70 cursor-not-allowed"
      >
        {body}
      </span>
    );
  }

  return (
    <Link
      key={cell.key}
      href={route(cell.routeName, site.id)}
      className={cn(
        'group flex flex-col justify-between gap-3 bg-card p-5 transition-colors min-h-[140px]',
        hasSignal ? 'hover:bg-accent/30' : 'hover:bg-muted/30',
      )}
      aria-label={`${cell.ctaLabel} — ${cell.title}`}
    >
      {body}
    </Link>
  );
}

export default function RecoverHub({ site, counts, pending_review_count: pendingReviewCount = 0, in_flight_batch: inFlightBatch = null }: Props) {
  const resolvedCounts = counts ?? ZERO_COUNTS;

  useEffect(() => {
    trackProductEvent(OPPORTUNITIES_HUB_VIEWED, { site_id: String(site.id) });
  }, [site.id]);

  // Primary CTA: pre-seed the highest-click-loss cohort in Content Inventory.
  const startRunHref = `${route('content-inventory.index', site.id)}?sort_by=click_loss&sort_direction=desc`;
  // R16-PD-02: resume lands on review.index so the user picks up where they left off.
  const resumeHref = route('review.index', site.id);

  // R16-PD-02: determine header CTA based on active batch.
  const isResuming = inFlightBatch !== null;
  const batchPendingCount = inFlightBatch?.pending_review_count ?? pendingReviewCount;
  const resumeLabel = batchPendingCount > 0
    ? `Resume — ${batchPendingCount} draft${batchPendingCount !== 1 ? 's' : ''} awaiting review`
    : 'Resume run';

  // DUX-EOU-02: split cells into primary (start-here) and secondary (deeper analysis).
  const primaryCells = cells.filter((c) => (PRIMARY_KEYS as readonly string[]).includes(c.key));
  const secondaryCells = cells.filter((c) => !(PRIMARY_KEYS as readonly string[]).includes(c.key));

  return (
    <DashboardLayout>
      <Head title={`Recover — ${site.name}`} />

      <EditorialPageHeader
        eyebrow={`Recover · ${site.name}`}
        title="Recover"
        subtitle="Find decaying pages, rewrite them, track the lift"
        readouts={
          site.has_gsc
            ? [
                {
                  label: 'Pending recs',
                  value: (
                    <span className="tabular-nums">
                      {resolvedCounts.pending_recommendations}
                    </span>
                  ),
                },
                {
                  label: 'Close to page 1',
                  value: (
                    <span className="flex items-center gap-1">
                      <span className="tabular-nums">
                        {resolvedCounts.keyword_opportunities}
                      </span>
                      <InfoTooltip
                        content="Pages ranking in positions 11–20 — one step from the first page. Small improvements can push them into top-10 results."
                        iconClassName="size-3"
                      />
                    </span>
                  ),
                },
              ]
            : undefined
        }
        actions={
          site.has_gsc && site.has_analysis ? (
            <div className="flex items-center gap-2">
              {/* R16-PD-02: Resume primary CTA when an active batch exists. */}
              {isResuming ? (
                <>
                  <Button asChild size="sm">
                    <Link href={resumeHref}>
                      <Play className="mr-1.5 size-4" />
                      {resumeLabel}
                    </Link>
                  </Button>
                  <Button asChild size="sm" variant="outline">
                    <Link href={startRunHref}>
                      Start new
                      <ArrowRight className="ml-1.5 size-4" />
                    </Link>
                  </Button>
                </>
              ) : (
                <Button asChild size="sm">
                  <Link href={startRunHref}>
                    Start Recovery Run
                    <ArrowRight className="ml-1.5 size-4" />
                  </Link>
                </Button>
              )}
            </div>
          ) : undefined
        }
      />

      <div className="container py-6 space-y-6">
        {!site.has_gsc ? (
          <HubDay0Empty
            headline={`Find pages losing traffic on ${site.name}.`}
            description="Connect Google Search Console and we'll surface pages close to the first page of Google, click-through gaps, pages competing with each other, and fixes ranked by traffic impact."
            ctaHref={route('onboarding.index', site.id)}
            ctaLabel="Connect Google Search Console"
            ctaSubLabel="Free, takes 2 minutes — read-only access."
            preview={cells.slice(0, 4).map((c) => ({
              title: c.title,
              description: c.description,
              Icon: c.icon,
            }))}
          />
        ) : (
          <div className="space-y-3">
            {/* DUX-EOU-02: Primary "start-here" cells — always visible. */}
            <div
              data-testid="hub-cell-grid"
              className="grid grid-cols-2 gap-px overflow-hidden rounded-2xl border bg-border"
            >
              {primaryCells.map((cell) => renderCell(cell, site, resolvedCounts))}
            </div>

            {/* DUX-EOU-02: Deeper-analysis cells — collapsed behind disclosure. */}
            {secondaryCells.length > 0 && (
              <SecondaryPanelToggle
                collapsedLabel="More tools"
                expandedLabel="Hide tools"
                regionLabel="Deeper analysis tools"
              >
                <div
                  data-testid="hub-secondary-cell-grid"
                  className="grid grid-cols-2 gap-px overflow-hidden rounded-2xl border bg-border lg:grid-cols-4"
                >
                  {secondaryCells.map((cell) => renderCell(cell, site, resolvedCounts))}
                </div>
              </SecondaryPanelToggle>
            )}
          </div>
        )}
      </div>
    </DashboardLayout>
  );
}
