/**
 * R8PROD-002 — Citation Prompt Manager
 *
 * Embedded management panel for citation-tracked prompts, rendered inside the
 * AiVisibilityCard on the ROI dashboard when the user expands "Manage prompts".
 *
 * Data flow:
 *   - On mount: lazy-fetches GET /sites/{site}/citation-prompts (JSON endpoint)
 *   - Mutations: Inertia router.post/patch/delete with preserveScroll so the
 *     card stays in view after each action.
 *
 * Empty states (per persona rule — act where you read):
 *   - No engine keys → "Connect an AI provider key" CTA to settings.ai
 *   - Keys present but zero prompts → seeding in-progress message
 *
 * Cap enforcement:
 *   - Per-engine cap displayed as "N of cap used"
 *   - Add form disabled when selected engine hits cap
 *
 * Engine hints:
 *   - Engines not in `availableEngines` show a "needs key" hint
 *   - Engine select is limited to `availableEngines` only
 */

import { Loader2, Pause, Play, Plus, Trash2 } from 'lucide-react';
import { toast } from 'sonner';

import { useCallback, useEffect, useRef, useState } from 'react';

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

import { Button } from '@/Components/ui/button';
import { useMutationButton } from '@/hooks/useMutationButton';
import { cn } from '@/lib/utils';

/* ── Types ─────────────────────────────────────────────────────────────── */

export interface CitationPromptRow {
  prompt_id: number;
  public_id: string;
  prompt: string;
  engine: string;
  cited: boolean;
  citation_rank: number | null;
  last_fetched_at: string | null;
  active: boolean;
}

interface IndexPayload {
  prompts: CitationPromptRow[];
  count: number;
  counts_by_engine: Record<string, number>;
  cap: number;
  engines: string[];
}

interface Props {
  siteId: number;
  /** Engines that the user has configured BYOK keys for (from RoiDashboardController) */
  availableEngines: string[];
  /**
   * Global per-engine cap from LimitService.
   * null means unlimited (enterprise tier) — the add form must remain enabled.
   * Pass null through; do NOT coerce to a default number in the parent (CODEX-P1a).
   */
  cap: number | null;
  /** Current active count per engine (for cap display + add-form gate) */
  countByEngine: Record<string, number>;
}

/* ── Constants ──────────────────────────────────────────────────────────── */

const ENGINE_LABELS: Record<string, string> = {
  google_aio: 'Google AIO',
  perplexity: 'Perplexity',
  chatgpt: 'ChatGPT',
  gemini: 'Gemini',
};

const ALL_ENGINE_KEYS = ['google_aio', 'perplexity', 'chatgpt'];

function engineLabel(engine: string): string {
  return ENGINE_LABELS[engine] ?? engine;
}

/* ── CitationPromptManager ───────────────────────────────────────────── */

export default function CitationPromptManager({
  siteId,
  availableEngines,
  cap,
  countByEngine,
}: Props) {
  /* ── state ─────────────────────────────────────────────────────────── */

  const [prompts, setPrompts] = useState<CitationPromptRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [fetchError, setFetchError] = useState(false);
  const [liveCountByEngine, setLiveCountByEngine] = useState<Record<string, number>>(countByEngine);
  const [liveCap, setLiveCap] = useState(cap);

  // Add form
  const [promptText, setPromptText] = useState('');
  const [selectedEngine, setSelectedEngine] = useState<string>(availableEngines[0] ?? 'google_aio');
  const [promptError, setPromptError] = useState<string | null>(null);
  const [submitting, setSubmitting] = useState(false);

  // Delete confirmation
  const [deleteTarget, setDeleteTarget] = useState<CitationPromptRow | null>(null);

  const isMounted = useRef(true);
  useEffect(() => {
    isMounted.current = true;
    return () => {
      isMounted.current = false;
    };
  }, []);

  /* ── Lazy-fetch prompt list ─────────────────────────────────────────── */

  const loadPrompts = useCallback(async () => {
    setLoading(true);
    setFetchError(false);
    try {
      const res = await fetch(route('citation-prompts.index', { site: siteId }), {
        headers: { Accept: 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
      });
      if (!res.ok) throw new Error('fetch failed');
      const data = (await res.json()) as IndexPayload;
      if (!isMounted.current) return;
      setPrompts(data.prompts);
      if (data.counts_by_engine) {
        setLiveCountByEngine(data.counts_by_engine);
      }
      if (typeof data.cap === 'number') {
        setLiveCap(data.cap);
      }
    } catch {
      if (isMounted.current) setFetchError(true);
    } finally {
      if (isMounted.current) setLoading(false);
    }
  }, [siteId]);

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

  /* ── Derived state ──────────────────────────────────────────────────── */

  const selectedEngineCount = liveCountByEngine[selectedEngine] ?? 0;
  const isCapReached = liveCap !== null && selectedEngineCount >= liveCap;

  /* ── Handlers ───────────────────────────────────────────────────────── */

  function handleAdd(e: React.FormEvent) {
    e.preventDefault();
    if (!promptText.trim()) {
      setPromptError('Prompt is required.');
      return;
    }
    setPromptError(null);
    setSubmitting(true);

    router.post(
      route('citation-prompts.store', { site: siteId }),
      { prompt: promptText.trim(), engine: selectedEngine },
      {
        preserveScroll: true,
        onSuccess: () => {
          setPromptText('');
          void loadPrompts();
        },
        onError: (errors: Record<string, string>) => {
          setPromptError(errors.prompt ?? 'Failed to add prompt.');
        },
        onFinish: () => {
          if (isMounted.current) setSubmitting(false);
        },
      },
    );
  }

  function handleDeleteConfirmed() {
    if (!deleteTarget) return;
    router.delete(
      route('citation-prompts.destroy', { site: siteId, prompt: deleteTarget.public_id }),
      {
        preserveScroll: true,
        onSuccess: () => {
          setDeleteTarget(null);
          void loadPrompts();
        },
        // QA-004: close the confirmation dialog on error (e.g. 404 from concurrent deletion)
        // so the user isn't stuck with an open confirmation. The prompt list will refresh.
        onError: () => {
          setDeleteTarget(null);
          void loadPrompts();
        },
      },
    );
  }

  /* ── Render: loading ────────────────────────────────────────────────── */

  if (loading) {
    return (
      <div className="flex items-center justify-center py-6" role="status" aria-label="Loading prompts">
        <Loader2 className="size-4 animate-spin text-muted-foreground" aria-hidden="true" />
        <span className="sr-only">Loading prompts…</span>
      </div>
    );
  }

  /* ── Render: fetch error ────────────────────────────────────────────── */

  if (fetchError) {
    return (
      <div className="rounded-md border border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive">
        Couldn't load prompts.{' '}
        <button
          type="button"
          className="underline underline-offset-2 hover:no-underline"
          onClick={() => void loadPrompts()}
        >
          Try again
        </button>
      </div>
    );
  }

  /* ── Render: no engine keys ─────────────────────────────────────────── */

  if (availableEngines.length === 0) {
    return (
      <div className="space-y-3 rounded-md border border-border bg-muted/30 p-4 text-sm">
        <p className="text-muted-foreground">
          Connect an AI provider key to start tracking how your site appears in AI answers.
        </p>
        <Link
          href={route('settings.ai')}
          className="inline-flex items-center gap-1.5 text-xs font-medium text-primary underline underline-offset-2 hover:no-underline"
          aria-label="Connect a key — opens AI settings"
        >
          Connect a key
        </Link>
      </div>
    );
  }

  /* ── Render: seeding in-progress (engine keys exist, zero prompts) ─── */

  if (availableEngines.length > 0 && prompts.length === 0) {
    return (
      <div className="space-y-4">
        <div className="rounded-md border border-border bg-muted/30 p-4 text-sm text-muted-foreground">
          <p className="font-medium text-foreground">Seeding prompts from your top pages…</p>
          <p className="mt-1 text-xs">
            Prompts are seeded automatically from your top GSC queries after the first weekly sync.
            You can also add your own below.
          </p>
        </div>
        {/* Allow manual add even during seeding */}
        <AddForm
          availableEngines={availableEngines}
          selectedEngine={selectedEngine}
          setSelectedEngine={setSelectedEngine}
          promptText={promptText}
          setPromptText={setPromptText}
          promptError={promptError}
          isCapReached={isCapReached}
          selectedEngineCount={selectedEngineCount}
          liveCap={liveCap}
          submitting={submitting}
          handleAdd={handleAdd}
        />
      </div>
    );
  }

  /* ── Render: full panel ─────────────────────────────────────────────── */

  // Engines not yet activated (key not configured)
  const inactiveEngines = ALL_ENGINE_KEYS.filter((k) => !availableEngines.includes(k));

  return (
    <div className="space-y-4" data-testid="citation-prompt-manager">
      {/* Prompt list */}
      <ul className="space-y-2" aria-label="Tracked prompts">
        {prompts.map((item) => (
          <li
            key={item.public_id}
            className={cn(
              'flex items-start gap-2 rounded-md border px-3 py-2 text-xs',
              item.active ? 'border-border bg-card' : 'border-border/50 bg-muted/30 opacity-60',
            )}
            data-testid={`prompt-row-${item.public_id}`}
          >
            {/* Cited badge */}
            <span
              className={cn(
                'mt-0.5 inline-flex shrink-0 items-center rounded-full px-2 py-0.5 font-medium',
                item.cited
                  ? 'bg-success/15 text-success-strong'
                  : 'bg-muted text-muted-foreground',
              )}
              aria-label={
                item.cited
                  ? item.citation_rank != null
                    ? `AI cited at rank ${item.citation_rank}`
                    : 'AI cited'
                  : 'Not cited'
              }
            >
              {item.cited
                ? item.citation_rank != null
                  ? `#${item.citation_rank} cited`
                  : 'AI cited'
                : 'Not cited'}
            </span>

            {/* Engine badge */}
            <span className="mt-0.5 shrink-0 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
              {engineLabel(item.engine)}
            </span>

            {/* Prompt text */}
            <span className="flex-1 truncate text-foreground" title={item.prompt}>
              {item.prompt}
            </span>

            {/* Actions */}
            <div className="ml-auto flex shrink-0 items-center gap-1">
              <PromptToggleButton
                siteId={siteId}
                prompt={item}
                onSuccess={() => void loadPrompts()}
              />

              <button
                type="button"
                onClick={() => setDeleteTarget(item)}
                className="inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
                aria-label={`Delete ${item.prompt}`}
                title="Delete"
              >
                <Trash2 className="size-3" aria-hidden="true" />
                <span className="sr-only">Delete</span>
                Delete
              </button>
            </div>
          </li>
        ))}
      </ul>

      {/* Per-engine hints for inactive engines */}
      {inactiveEngines.length > 0 && (
        <div className="space-y-1">
          {inactiveEngines.map((engine) => (
            <p key={engine} className="text-[10px] text-muted-foreground/70">
              {engineLabel(engine)} — add a{' '}
              <Link href={route('settings.ai')} className="underline underline-offset-2">
                {engineLabel(engine)} key
              </Link>{' '}
              to activate
            </p>
          ))}
          <p className="text-[10px] text-muted-foreground/50">
            Perplexity requires a Perplexity API key; ChatGPT requires an OpenAI key; Google AIO
            requires a DataForSEO key.
          </p>
        </div>
      )}

      {/* Add form */}
      <AddForm
        availableEngines={availableEngines}
        selectedEngine={selectedEngine}
        setSelectedEngine={setSelectedEngine}
        promptText={promptText}
        setPromptText={setPromptText}
        promptError={promptError}
        isCapReached={isCapReached}
        selectedEngineCount={selectedEngineCount}
        liveCap={liveCap}
        submitting={submitting}
        handleAdd={handleAdd}
      />

      {/* Delete confirmation overlay */}
      {deleteTarget !== null && (
        <DeleteConfirmation
          prompt={deleteTarget}
          onConfirm={handleDeleteConfirmed}
          onCancel={() => setDeleteTarget(null)}
        />
      )}
    </div>
  );
}

/* ── PromptToggleButton sub-component ───────────────────────────────────── */

interface PromptToggleButtonProps {
  siteId: number;
  prompt: CitationPromptRow;
  onSuccess: () => void;
}

/**
 * Per-row active toggle button.
 *
 * Owns its own useMutationButton so the processing/disabled state is isolated
 * to the clicked row — clicking Pause on row A does not lock row B.
 *
 * R11FE-002: adds request-lifecycle loading state (disabled while in-flight)
 * and onError so a failed PATCH surfaces a toast instead of silently leaving
 * the toggle in its pre-click state.
 */
function PromptToggleButton({ siteId, prompt, onSuccess }: PromptToggleButtonProps) {
  const { processing, onClick } = useMutationButton(
    (opts) =>
      router.patch(
        route('citation-prompts.update', { site: siteId, prompt: prompt.public_id }),
        { active: !prompt.active },
        {
          ...opts,
          preserveScroll: true,
          onSuccess: () => {
            onSuccess();
          },
        },
      ),
    {
      onError: () => {
        toast.error('Could not update the prompt — please try again.');
      },
    },
  );

  return (
    <button
      type="button"
      onClick={onClick}
      disabled={processing}
      className="inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-[10px] text-muted-foreground hover:bg-accent hover:text-foreground disabled:pointer-events-none disabled:opacity-50"
      aria-label={prompt.active ? `Pause ${prompt.prompt}` : `Resume ${prompt.prompt}`}
      title={prompt.active ? 'Pause' : 'Resume'}
    >
      {prompt.active ? (
        <Pause className="size-3" aria-hidden="true" />
      ) : (
        <Play className="size-3" aria-hidden="true" />
      )}
      <span className="sr-only">{prompt.active ? 'Pause' : 'Resume'}</span>
      {prompt.active ? 'Pause' : 'Resume'}
    </button>
  );
}

/* ── AddForm sub-component ──────────────────────────────────────────────── */

interface AddFormProps {
  availableEngines: string[];
  selectedEngine: string;
  setSelectedEngine: (v: string) => void;
  promptText: string;
  setPromptText: (v: string) => void;
  promptError: string | null;
  isCapReached: boolean;
  selectedEngineCount: number;
  liveCap: number | null;
  submitting: boolean;
  handleAdd: (e: React.FormEvent) => void;
}

function AddForm({
  availableEngines,
  selectedEngine,
  setSelectedEngine,
  promptText,
  setPromptText,
  promptError,
  isCapReached,
  selectedEngineCount,
  liveCap,
  submitting,
  handleAdd,
}: AddFormProps) {
  return (
    <form onSubmit={handleAdd} className="space-y-2 border-t pt-3">
      <div className="flex gap-2">
        {/* Engine select */}
        <div className="shrink-0">
          <label htmlFor="citation-engine-select" className="sr-only">
            Engine
          </label>
          <select
            id="citation-engine-select"
            aria-label="Engine"
            value={selectedEngine}
            onChange={(e) => setSelectedEngine(e.target.value)}
            className="h-8 rounded-md border border-input bg-background px-2 text-xs text-foreground focus:outline-none focus:ring-1 focus:ring-ring"
            disabled={availableEngines.length === 0}
          >
            {availableEngines.map((engine) => (
              <option key={engine} value={engine}>
                {engineLabel(engine)}
              </option>
            ))}
          </select>
        </div>

        {/* Prompt input */}
        <div className="flex-1">
          <label htmlFor="citation-prompt-input" className="sr-only">
            Prompt text
          </label>
          <input
            id="citation-prompt-input"
            type="text"
            value={promptText}
            onChange={(e) => setPromptText(e.target.value)}
            placeholder="Enter a prompt to track (e.g. best SEO tools 2026)"
            disabled={isCapReached || submitting}
            className={cn(
              'h-8 w-full rounded-md border bg-background px-3 text-xs placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring',
              promptError ? 'border-destructive' : 'border-input',
            )}
            aria-describedby={promptError ? 'citation-prompt-error' : undefined}
          />
        </div>

        {/* Submit */}
        <Button
          type="submit"
          size="sm"
          variant="outline"
          disabled={isCapReached || submitting}
          className="h-8 shrink-0 text-xs"
          aria-label="Add prompt"
        >
          <Plus className="mr-1 size-3" aria-hidden="true" />
          Add prompt
        </Button>
      </div>

      {/* Cap indicator */}
      {isCapReached ? (
        <p className="text-[10px] text-muted-foreground">
          {selectedEngineCount} of {liveCap} used — upgrade your plan to track more prompts.
        </p>
      ) : (
        liveCap !== null && (
          <p className="text-[10px] text-muted-foreground/60">
            {selectedEngineCount} of {liveCap} used for {engineLabel(selectedEngine)}
          </p>
        )
      )}

      {/* Validation error */}
      {promptError && (
        <p id="citation-prompt-error" className="text-[10px] text-destructive" role="alert">
          {promptError}
        </p>
      )}
    </form>
  );
}

/* ── DeleteConfirmation sub-component ───────────────────────────────────── */

interface DeleteConfirmationProps {
  prompt: CitationPromptRow;
  onConfirm: () => void;
  onCancel: () => void;
}

function DeleteConfirmation({ prompt, onConfirm, onCancel }: DeleteConfirmationProps) {
  return (
    <div
      role="alertdialog"
      aria-label="Delete confirmation"
      className="rounded-md border border-destructive/30 bg-destructive/5 p-3 text-xs"
    >
      <p className="mb-3 text-foreground">
        Remove <strong className="font-medium">{prompt.prompt}</strong>? It won&rsquo;t be
        included in future AI drafts for this page, and all citation snapshots for this prompt
        will be deleted.
      </p>
      <div className="flex gap-2">
        <Button
          type="button"
          size="sm"
          variant="destructive"
          className="h-7 text-xs"
          onClick={onConfirm}
          aria-label="Confirm delete"
        >
          Confirm
        </Button>
        <Button
          type="button"
          size="sm"
          variant="outline"
          className="h-7 text-xs"
          onClick={onCancel}
          aria-label="Cancel delete"
        >
          Cancel
        </Button>
      </div>
    </div>
  );
}
