import { ReactNode, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { ArrowLeft, ArrowRight, Check, ChevronLeft } from "lucide-react";

import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";

/**
 * Generic multi-step wizard shell.
 *
 * A wizard is defined as an ordered array of {@link WizardStepDef} items.
 * Each step owns its own piece of the accumulated state and returns a node
 * that renders the form. The shell handles:
 *
 *   - rendering the numbered stepper (with `completed` / `active` styles)
 *   - Back / Next navigation
 *   - a final "Finish" button that calls `onFinish(state)`
 *   - per-step validation via `step.validate(state)` → return an error
 *     message or `null` when valid
 *   - a persistent progress bar
 *
 * Keeping this shell free of domain logic means every scenario wizard
 * (rubric, course, structure, …) is just a tiny file that defines steps
 * and wires the final submit to the right service.
 */

export interface WizardStepDef<TState> {
  id: string;
  /** i18n key for the step title. */
  titleKey: string;
  /** Optional i18n key for a short description shown under the title. */
  descriptionKey?: string;
  /**
   * Render the step's form. Call `update(patch)` to merge fields into the
   * shared state. Do **not** call `onNext` directly — the shell handles
   * Next/Back; the render function should just update local fields.
   */
  render: (props: StepRenderProps<TState>) => ReactNode;
  /**
   * Synchronous validator. Return a human-readable error message that will
   * be displayed under the form and block Next, or `null` when valid.
   */
  validate?: (state: TState) => string | null;
}

export interface StepRenderProps<TState> {
  state: TState;
  update: (patch: Partial<TState>) => void;
  error: string | null;
}

export interface StepWizardProps<TState> {
  /** i18n key for the wizard heading. */
  titleKey: string;
  /** i18n key for the short lead paragraph shown under the heading. */
  subtitleKey?: string;
  /** Optional back-link to the hub. */
  backTo?: string;
  backLabelKey?: string;
  steps: WizardStepDef<TState>[];
  initialState: TState;
  /** Called once the user clicks Finish on the last step. */
  onFinish: (state: TState) => Promise<void> | void;
  /** i18n key for the Finish button label. Defaults to "wizard.finish". */
  finishLabelKey?: string;
  /** Disable all form controls (used by consumers while submitting). */
  submitting?: boolean;
}

export function StepWizard<TState>({
  titleKey,
  subtitleKey,
  backTo,
  backLabelKey = "wizard.backToHub",
  steps,
  initialState,
  onFinish,
  finishLabelKey = "wizard.finish",
  submitting = false,
}: StepWizardProps<TState>) {
  const { t } = useTranslation();
  const [index, setIndex] = useState(0);
  const [state, setState] = useState<TState>(initialState);
  const [error, setError] = useState<string | null>(null);
  const [submittingInternal, setSubmittingInternal] = useState(false);

  const busy = submitting || submittingInternal;
  const current = steps[index];
  const isLast = index === steps.length - 1;
  const progress = Math.round(((index + 1) / steps.length) * 100);

  const update = (patch: Partial<TState>) => {
    setState((prev) => ({ ...prev, ...patch }));
    // Clear validation error as soon as the user starts typing again.
    if (error) setError(null);
  };

  const validateAndAdvance = async () => {
    const msg = current.validate?.(state) ?? null;
    if (msg) {
      setError(msg);
      return;
    }
    setError(null);

    if (!isLast) {
      setIndex((i) => i + 1);
      return;
    }

    // Last step: submit.
    try {
      setSubmittingInternal(true);
      await onFinish(state);
    } finally {
      setSubmittingInternal(false);
    }
  };

  const stepStatus = useMemo(
    () =>
      steps.map((_, i) => {
        if (i < index) return "done" as const;
        if (i === index) return "active" as const;
        return "upcoming" as const;
      }),
    [steps, index],
  );

  return (
    <div className="mx-auto max-w-3xl space-y-6">
      {/* Heading + back link */}
      <div className="space-y-2">
        {backTo && (
          <Button variant="ghost" size="sm" asChild className="-ml-2 text-muted-foreground">
            <Link to={backTo} className="flex items-center gap-1">
              <ChevronLeft className="h-4 w-4" />
              {t(backLabelKey)}
            </Link>
          </Button>
        )}
        <h1 className="text-2xl font-semibold tracking-tight">{t(titleKey)}</h1>
        {subtitleKey && <p className="text-muted-foreground">{t(subtitleKey)}</p>}
      </div>

      {/* Stepper */}
      <ol className="flex items-center gap-2 overflow-x-auto pb-2" role="list">
        {steps.map((step, i) => {
          const s = stepStatus[i];
          return (
            <li key={step.id} className="flex items-center gap-2 shrink-0">
              <div
                className={cn(
                  "flex h-7 w-7 items-center justify-center rounded-full text-xs font-semibold",
                  s === "done" && "bg-primary text-primary-foreground",
                  s === "active" && "bg-primary/10 text-primary ring-2 ring-primary",
                  s === "upcoming" && "bg-muted text-muted-foreground",
                )}
                aria-current={s === "active" ? "step" : undefined}
              >
                {s === "done" ? <Check className="h-4 w-4" /> : i + 1}
              </div>
              <span
                className={cn(
                  "text-sm whitespace-nowrap",
                  s === "active" ? "font-medium" : "text-muted-foreground",
                )}
              >
                {t(step.titleKey)}
              </span>
              {i < steps.length - 1 && <div className="mx-1 h-px w-8 bg-border" />}
            </li>
          );
        })}
      </ol>

      {/* Progress bar */}
      <div className="h-1.5 w-full rounded-full bg-muted overflow-hidden">
        <div
          className="h-full bg-primary transition-all"
          style={{ width: `${progress}%` }}
          aria-hidden
        />
      </div>

      {/* Active step */}
      <Card>
        <CardHeader className="pb-2">
          <h2 className="text-lg font-semibold">{t(current.titleKey)}</h2>
          {current.descriptionKey && (
            <p className="text-sm text-muted-foreground">{t(current.descriptionKey)}</p>
          )}
        </CardHeader>
        <CardContent className="space-y-4">
          {current.render({ state, update, error })}

          {error && (
            <div
              role="alert"
              className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive"
            >
              {error}
            </div>
          )}
        </CardContent>
      </Card>

      {/* Navigation */}
      <div className="flex items-center justify-between gap-2">
        <Button
          variant="outline"
          onClick={() => setIndex((i) => Math.max(0, i - 1))}
          disabled={busy || index === 0}
        >
          <ArrowLeft className="mr-1 h-4 w-4" />
          {t("wizard.back")}
        </Button>

        <div className="text-xs text-muted-foreground tabular-nums">
          {t("wizard.stepOf", { current: index + 1, total: steps.length })}
        </div>

        <Button onClick={validateAndAdvance} disabled={busy}>
          {isLast ? t(finishLabelKey) : t("wizard.next")}
          {!isLast && <ArrowRight className="ml-1 h-4 w-4" />}
        </Button>
      </div>
    </div>
  );
}

export default StepWizard;
