import { useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import {
  FileText,
  Image as ImageIcon,
  Library,
  Link2,
  Mic,
  Music,
  Trash2,
  Upload,
  Video,
  X,
} from "lucide-react";

import { StepWizard, type WizardStepDef } from "@/components/wizard/StepWizard";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { LibraryPickerDialog } from "@/components/coursePlan/LibraryPickerDialog";
import { coursePlanService } from "@/services/coursePlan.service";
import { describeApiError } from "@/lib/api-client";
import type { CoursePlanGenerateBrief, Resource } from "@/types";

/**
 * AI course-plan generation wizard.
 *
 * Four steps:
 *   1. Basics         — title, CEFR level, total weeks, contact hours.
 *   2. Coverage       — skills division (e.g. "10 hrs Reading+Writing /
 *                       8 hrs Listening+Speaking"), learner profile.
 *   3. Scope          — grammar focus chips, resource citations chips,
 *                       optional free-form notes.
 *   4. Review         — finish → POST /api/ai/course-plan which triggers
 *                       the OpenAI call, persists the plan, and returns
 *                       the finished record; we navigate to the detail
 *                       page so the user can start generating Week 1
 *                       materials immediately.
 */

type DraftSourceKind = "file" | "url" | "text" | "resource";

interface DraftSource {
  /** Stable client-side id; not persisted. */
  uid: string;
  kind: DraftSourceKind;
  name: string;
  /** Only set when kind === "file". */
  file?: File;
  /** Only set when kind === "url". */
  url?: string;
  /** Only set when kind === "text". */
  text?: string;
  /** Only set when kind === "resource" — pointer into the central library. */
  resourceId?: number;
  /** Display-only metadata so we can render the row without a refetch. */
  resourceType?: string;
}

interface MediaToggleState {
  /** Generate narration audio (Polly / ElevenLabs) for listening +
   * speaking materials right after each week's materials are produced. */
  audio: boolean;
  /** Generate DALL-E images for reading texts, listening scripts, and
   * vocabulary lists. Bound to the per-plan image budget. */
  image: boolean;
  /** Compose audio + image into a slideshow MP4 with ffmpeg. Slowest
   * option; off by default. */
  video: boolean;
}

interface CoursePlanWizardState {
  title: string;
  cefr_level: string;
  total_weeks: number;
  contact_hours_per_week: number;
  skills_division: string;
  learner_profile: string;
  grammar_focus: string[];
  resources: string[];
  notes: string;
  sources: DraftSource[];
  media: MediaToggleState;
}

const CEFR_OPTIONS = [
  { value: "pre_a1", label: "Pre-A1" },
  { value: "a1", label: "A1" },
  { value: "a2", label: "A2" },
  { value: "b1", label: "B1" },
  { value: "b2", label: "B2" },
  { value: "c1", label: "C1" },
  { value: "c2", label: "C2" },
];

export default function CoursePlanWizard() {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const qc = useQueryClient();

  const mutation = useMutation({
    mutationFn: (brief: CoursePlanGenerateBrief) =>
      coursePlanService.generate(brief),
    onSuccess: (resp) => {
      qc.invalidateQueries({ queryKey: ["course-plans"] });
      toast.success(t("coursePlan.generateSuccess"));
      const id = resp?.data?.id;
      if (id) navigate(`/admin/course-plans/${id}`);
      else navigate("/admin/course-plans");
    },
    onError: (err) => {
      toast.error(describeApiError(err, t("coursePlan.generateFailed")));
    },
  });

  const steps: WizardStepDef<CoursePlanWizardState>[] = [
    {
      id: "basics",
      titleKey: "coursePlan.wizard.steps.basics",
      descriptionKey: "coursePlan.wizard.steps.basicsDesc",
      validate: (s) => {
        if (!s.title.trim()) return t("coursePlan.wizard.errors.titleRequired");
        if (!s.cefr_level) return t("coursePlan.wizard.errors.cefrRequired");
        if (!s.total_weeks || s.total_weeks < 1)
          return t("coursePlan.wizard.errors.weeksRange");
        return null;
      },
      render: ({ state, update }) => (
        <div className="grid gap-4 sm:grid-cols-2">
          <div className="sm:col-span-2">
            <Label htmlFor="title">{t("coursePlan.wizard.fields.title")}</Label>
            <Input
              id="title"
              placeholder="General English 1"
              value={state.title}
              onChange={(e) => update({ title: e.target.value })}
            />
          </div>
          <div>
            <Label>{t("coursePlan.wizard.fields.cefr")}</Label>
            <Select
              value={state.cefr_level}
              onValueChange={(v) => update({ cefr_level: v })}
            >
              <SelectTrigger>
                <SelectValue placeholder={t("coursePlan.wizard.fields.cefrPlaceholder")} />
              </SelectTrigger>
              <SelectContent>
                {CEFR_OPTIONS.map((o) => (
                  <SelectItem key={o.value} value={o.value}>
                    {o.label}
                  </SelectItem>
                ))}
              </SelectContent>
            </Select>
          </div>
          <div>
            <Label htmlFor="weeks">{t("coursePlan.wizard.fields.totalWeeks")}</Label>
            <Input
              id="weeks"
              type="number"
              min={1}
              max={30}
              value={state.total_weeks}
              onChange={(e) => update({ total_weeks: Number(e.target.value) || 0 })}
            />
          </div>
          <div>
            <Label htmlFor="hours">{t("coursePlan.wizard.fields.contactHours")}</Label>
            <Input
              id="hours"
              type="number"
              min={1}
              max={40}
              value={state.contact_hours_per_week}
              onChange={(e) =>
                update({ contact_hours_per_week: Number(e.target.value) || 0 })
              }
            />
          </div>
        </div>
      ),
    },
    {
      id: "coverage",
      titleKey: "coursePlan.wizard.steps.coverage",
      descriptionKey: "coursePlan.wizard.steps.coverageDesc",
      render: ({ state, update }) => (
        <div className="grid gap-4">
          <div>
            <Label htmlFor="skills">
              {t("coursePlan.wizard.fields.skillsDivision")}
            </Label>
            <Input
              id="skills"
              placeholder="10 hrs/wk Reading & Writing + 8 hrs/wk Listening & Speaking"
              value={state.skills_division}
              onChange={(e) => update({ skills_division: e.target.value })}
            />
            <p className="mt-1 text-xs text-muted-foreground">
              {t("coursePlan.wizard.fields.skillsDivisionHint")}
            </p>
          </div>
          <div>
            <Label htmlFor="profile">
              {t("coursePlan.wizard.fields.learnerProfile")}
            </Label>
            <Textarea
              id="profile"
              rows={3}
              placeholder={t("coursePlan.wizard.fields.learnerProfilePlaceholder")}
              value={state.learner_profile}
              onChange={(e) => update({ learner_profile: e.target.value })}
            />
          </div>
        </div>
      ),
    },
    {
      id: "sources",
      titleKey: "coursePlan.wizard.steps.sources",
      descriptionKey: "coursePlan.wizard.steps.sourcesDesc",
      render: ({ state, update }) => (
        <SourcesStep
          sources={state.sources}
          onChange={(sources) => update({ sources })}
        />
      ),
    },
    {
      id: "scope",
      titleKey: "coursePlan.wizard.steps.scope",
      descriptionKey: "coursePlan.wizard.steps.scopeDesc",
      render: ({ state, update }) => (
        <div className="grid gap-4">
          <ChipInput
            label={t("coursePlan.wizard.fields.grammarFocus")}
            placeholder={t("coursePlan.wizard.fields.grammarFocusPlaceholder")}
            values={state.grammar_focus}
            onChange={(next) => update({ grammar_focus: next })}
          />
          <ChipInput
            label={t("coursePlan.wizard.fields.resources")}
            placeholder={t("coursePlan.wizard.fields.resourcesPlaceholder")}
            values={state.resources}
            onChange={(next) => update({ resources: next })}
          />
          <div>
            <Label htmlFor="notes">{t("coursePlan.wizard.fields.notes")}</Label>
            <Textarea
              id="notes"
              rows={3}
              placeholder={t("coursePlan.wizard.fields.notesPlaceholder")}
              value={state.notes}
              onChange={(e) => update({ notes: e.target.value })}
            />
          </div>
        </div>
      ),
    },
    {
      id: "media",
      titleKey: "coursePlan.wizard.steps.media",
      descriptionKey: "coursePlan.wizard.steps.mediaDesc",
      render: ({ state, update }) => (
        <MediaStep
          media={state.media}
          onChange={(media) => update({ media })}
        />
      ),
    },
    {
      id: "review",
      titleKey: "coursePlan.wizard.steps.review",
      descriptionKey: "coursePlan.wizard.steps.reviewDesc",
      render: ({ state }) => (
        <div className="space-y-3 text-sm">
          <ReviewRow label={t("coursePlan.wizard.fields.title")} value={state.title} />
          <ReviewRow
            label={t("coursePlan.wizard.fields.cefr")}
            value={
              CEFR_OPTIONS.find((o) => o.value === state.cefr_level)?.label ||
              state.cefr_level ||
              "—"
            }
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.totalWeeks")}
            value={String(state.total_weeks || "—")}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.contactHours")}
            value={String(state.contact_hours_per_week || "—")}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.skillsDivision")}
            value={state.skills_division || "—"}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.learnerProfile")}
            value={state.learner_profile || "—"}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.grammarFocus")}
            value={state.grammar_focus.join(", ") || "—"}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.resources")}
            value={state.resources.join(", ") || "—"}
          />
          <ReviewRow
            label={t("coursePlan.wizard.fields.notes")}
            value={state.notes || "—"}
          />
          <ReviewRow
            label={t("coursePlan.wizard.steps.sources")}
            value={
              state.sources.length
                ? t("coursePlan.wizard.review.sourcesCount", {
                    count: state.sources.length,
                  })
                : t("coursePlan.wizard.review.noSources")
            }
          />
          <ReviewRow
            label={t("coursePlan.wizard.steps.media")}
            value={[
              state.media.audio ? t("coursePlan.media.audio") : null,
              state.media.image ? t("coursePlan.media.image") : null,
              state.media.video ? t("coursePlan.media.video") : null,
            ]
              .filter(Boolean)
              .join(", ") || t("coursePlan.wizard.review.mediaNone")}
          />
          <div className="rounded-md border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
            {t("coursePlan.wizard.reviewHint")}
          </div>
        </div>
      ),
    },
  ];

  return (
    <StepWizard<CoursePlanWizardState>
      titleKey="coursePlan.wizard.title"
      subtitleKey="coursePlan.wizard.subtitle"
      backTo="/admin/smart-wizard"
      steps={steps}
      submitting={mutation.isPending}
      finishLabelKey="coursePlan.wizard.finish"
      initialState={{
        title: "",
        cefr_level: "a2",
        total_weeks: 12,
        contact_hours_per_week: 18,
        skills_division: "",
        learner_profile: "",
        grammar_focus: [],
        resources: [],
        notes: "",
        sources: [],
        media: { audio: true, image: true, video: false },
      }}
      onFinish={async (state) => {
        const resp = await mutation.mutateAsync({
          title: state.title.trim(),
          cefr_level: state.cefr_level,
          total_weeks: state.total_weeks,
          contact_hours_per_week: state.contact_hours_per_week,
          skills_division: state.skills_division.trim() || undefined,
          learner_profile: state.learner_profile.trim() || undefined,
          grammar_focus: state.grammar_focus.length
            ? state.grammar_focus
            : undefined,
          resources: state.resources.length ? state.resources : undefined,
          notes: state.notes.trim() || undefined,
        });
        const planId = resp?.data?.id;
        if (planId && state.sources.length) {
          // Library picks go through the dedicated bulk endpoint — one
          // round-trip for the whole set, with server-side dedupe — so we
          // collect their ids first and post them together. The remaining
          // file/url/text drafts are posted individually as before.
          const resourceIds = state.sources
            .filter((s) => s.kind === "resource" && s.resourceId)
            .map((s) => s.resourceId as number);
          if (resourceIds.length) {
            try {
              await coursePlanService.attachResources(planId, resourceIds);
            } catch (err) {
              toast.error(
                describeApiError(
                  err,
                  t("coursePlan.sources.libraryAttachFailed"),
                ),
              );
            }
          }

          // Best-effort upload — we keep going even if a single one fails so
          // the user lands on the detail page with whatever did make it in.
          for (const src of state.sources) {
            try {
              if (src.kind === "file" && src.file) {
                await coursePlanService.uploadSource(planId, src.file, {
                  name: src.name || src.file.name,
                });
              } else if (src.kind === "url" && src.url) {
                await coursePlanService.createSource(planId, {
                  kind: "url",
                  url: src.url,
                  name: src.name || src.url,
                });
              } else if (src.kind === "text" && src.text) {
                await coursePlanService.createSource(planId, {
                  kind: "text",
                  inline_text: src.text,
                  name: src.name || t("coursePlan.sourceKind.text"),
                });
              }
              // src.kind === "resource" was already handled in the bulk
              // attach above.
            } catch (err) {
              toast.error(
                describeApiError(err, t("coursePlan.sources.uploadFailed", {
                  name: src.name || src.kind,
                })),
              );
            }
          }
        }
      }}
    />
  );
}

function genUid() {
  return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
}

function SourcesStep({
  sources,
  onChange,
}: {
  sources: DraftSource[];
  onChange: (next: DraftSource[]) => void;
}) {
  const { t } = useTranslation();
  const fileRef = useRef<HTMLInputElement>(null);
  const [draftUrl, setDraftUrl] = useState("");
  const [draftText, setDraftText] = useState("");
  const [libraryOpen, setLibraryOpen] = useState(false);

  // Resource ids already queued in this wizard session; passed to the
  // picker so the same resource can't be added twice.
  const queuedResourceIds = new Set(
    sources
      .filter((s) => s.kind === "resource" && s.resourceId)
      .map((s) => s.resourceId as number),
  );

  const addFiles = (files: FileList | null) => {
    if (!files || !files.length) return;
    const additions: DraftSource[] = [];
    for (const f of Array.from(files)) {
      additions.push({
        uid: genUid(),
        kind: "file",
        name: f.name,
        file: f,
      });
    }
    onChange([...sources, ...additions]);
  };

  const addUrl = () => {
    const url = draftUrl.trim();
    if (!url) return;
    onChange([...sources, { uid: genUid(), kind: "url", name: url, url }]);
    setDraftUrl("");
  };

  const addText = () => {
    const text = draftText.trim();
    if (!text) return;
    onChange([
      ...sources,
      {
        uid: genUid(),
        kind: "text",
        name: text.slice(0, 60).replace(/\s+/g, " "),
        text,
      },
    ]);
    setDraftText("");
  };

  const addLibraryResources = (picked: Resource[]) => {
    if (!picked.length) return;
    const additions: DraftSource[] = picked.map((r) => ({
      uid: genUid(),
      kind: "resource",
      name: r.name,
      resourceId: r.id,
      resourceType: r.resource_type || r.type || "resource",
    }));
    onChange([...sources, ...additions]);
    setLibraryOpen(false);
  };

  const remove = (uid: string) => onChange(sources.filter((s) => s.uid !== uid));

  return (
    <div className="space-y-5">
      <div className="grid gap-3 md:grid-cols-2">
        <div className="rounded-md border-dashed border-2 px-4 py-6 text-center bg-muted/20">
          <FileText className="mx-auto h-6 w-6 text-muted-foreground" />
          <p className="mt-2 text-sm font-medium">
            {t("coursePlan.sources.dropTitle")}
          </p>
          <p className="text-xs text-muted-foreground">
            {t("coursePlan.sources.dropHint")}
          </p>
          <div className="mt-3">
            <input
              ref={fileRef}
              type="file"
              multiple
              className="hidden"
              accept=".pdf,.doc,.docx,.txt,.md,.html,.htm,.rtf"
              onChange={(e) => {
                addFiles(e.currentTarget.files);
                if (fileRef.current) fileRef.current.value = "";
              }}
            />
            <Button
              variant="outline"
              size="sm"
              onClick={() => fileRef.current?.click()}
            >
              <Upload className="mr-1 h-4 w-4" />
              {t("coursePlan.sources.uploadFiles")}
            </Button>
          </div>
        </div>

        <div className="rounded-md border-dashed border-2 px-4 py-6 text-center bg-muted/20">
          <Library className="mx-auto h-6 w-6 text-muted-foreground" />
          <p className="mt-2 text-sm font-medium">
            {t("coursePlan.sources.libraryPickTitle")}
          </p>
          <p className="text-xs text-muted-foreground">
            {t("coursePlan.sources.libraryPickHint")}
          </p>
          <div className="mt-3">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setLibraryOpen(true)}
            >
              <Library className="mr-1 h-4 w-4" />
              {t("coursePlan.sources.libraryPickButton")}
            </Button>
          </div>
        </div>
      </div>

      <div className="grid gap-3 sm:grid-cols-2">
        <div>
          <Label>{t("coursePlan.sources.urlLabel")}</Label>
          <div className="mt-1 flex gap-2">
            <Input
              value={draftUrl}
              onChange={(e) => setDraftUrl(e.target.value)}
              placeholder="https://example.com/article"
              onKeyDown={(e) => {
                if (e.key === "Enter") {
                  e.preventDefault();
                  addUrl();
                }
              }}
            />
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={addUrl}
              disabled={!draftUrl.trim()}
            >
              <Link2 className="h-4 w-4" />
            </Button>
          </div>
        </div>
        <div>
          <Label>{t("coursePlan.sources.textLabel")}</Label>
          <div className="mt-1 flex gap-2">
            <Textarea
              rows={3}
              value={draftText}
              onChange={(e) => setDraftText(e.target.value)}
              placeholder={t("coursePlan.sources.textPlaceholder")}
            />
            <Button
              type="button"
              variant="outline"
              size="sm"
              onClick={addText}
              disabled={!draftText.trim()}
              className="self-start"
            >
              <FileText className="h-4 w-4" />
            </Button>
          </div>
        </div>
      </div>

      {sources.length > 0 && (
        <div className="rounded-md border">
          <div className="px-3 py-2 border-b bg-muted/30 text-xs uppercase tracking-wide text-muted-foreground">
            {t("coursePlan.sources.collected", { count: sources.length })}
          </div>
          <ul className="divide-y">
            {sources.map((s) => (
              <li
                key={s.uid}
                className="flex items-center gap-2 px-3 py-2 text-sm"
              >
                <Badge variant="outline" className="capitalize text-[10px]">
                  {s.kind === "resource"
                    ? s.resourceType || "library"
                    : s.kind}
                </Badge>
                {s.kind === "resource" && (
                  <Badge
                    variant="secondary"
                    className="gap-1 text-[10px] flex items-center"
                  >
                    <Library className="h-3 w-3" />
                    {t("coursePlan.sources.fromLibrary")}
                  </Badge>
                )}
                <span className="flex-1 truncate">{s.name}</span>
                <Button
                  variant="ghost"
                  size="icon"
                  className="h-7 w-7"
                  onClick={() => remove(s.uid)}
                  aria-label={t("common.remove", "Remove")}
                >
                  <Trash2 className="h-4 w-4" />
                </Button>
              </li>
            ))}
          </ul>
        </div>
      )}

      <LibraryPickerDialog
        open={libraryOpen}
        onOpenChange={setLibraryOpen}
        alreadyLinkedIds={queuedResourceIds}
        onConfirm={addLibraryResources}
      />
    </div>
  );
}

function MediaStep({
  media,
  onChange,
}: {
  media: MediaToggleState;
  onChange: (next: MediaToggleState) => void;
}) {
  const { t } = useTranslation();
  return (
    <div className="grid gap-3 sm:grid-cols-3">
      <MediaToggleCard
        icon={Music}
        title={t("coursePlan.media.audioTitle")}
        description={t("coursePlan.media.audioDesc")}
        checked={media.audio}
        onToggle={(v) => onChange({ ...media, audio: v })}
      />
      <MediaToggleCard
        icon={ImageIcon}
        title={t("coursePlan.media.imageTitle")}
        description={t("coursePlan.media.imageDesc")}
        checked={media.image}
        onToggle={(v) => onChange({ ...media, image: v })}
      />
      <MediaToggleCard
        icon={Video}
        title={t("coursePlan.media.videoTitle")}
        description={t("coursePlan.media.videoDesc")}
        checked={media.video}
        onToggle={(v) => onChange({ ...media, video: v })}
      />
      <div className="sm:col-span-3 text-xs text-muted-foreground flex items-center gap-2">
        <Mic className="h-3.5 w-3.5" />
        {t("coursePlan.media.hint")}
      </div>
    </div>
  );
}

function MediaToggleCard({
  icon: Icon,
  title,
  description,
  checked,
  onToggle,
}: {
  icon: typeof Music;
  title: string;
  description: string;
  checked: boolean;
  onToggle: (v: boolean) => void;
}) {
  return (
    <button
      type="button"
      onClick={() => onToggle(!checked)}
      className={[
        "rounded-md border p-3 text-left transition-colors",
        checked
          ? "border-primary bg-primary/5"
          : "border-border hover:bg-muted/40",
      ].join(" ")}
    >
      <div className="flex items-center gap-2">
        <Icon className="h-4 w-4 text-primary" />
        <div className="font-medium text-sm">{title}</div>
      </div>
      <p className="mt-1 text-xs text-muted-foreground">{description}</p>
    </button>
  );
}

function ReviewRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="grid grid-cols-3 gap-2">
      <div className="col-span-1 text-muted-foreground">{label}</div>
      <div className="col-span-2 font-medium break-words">{value}</div>
    </div>
  );
}

/**
 * Tiny chip input: press Enter (or comma) to commit a value. Used for
 * grammar focus items and resource citations. Kept local because it's
 * not worth promoting to a reusable component yet.
 */
function ChipInput({
  label,
  placeholder,
  values,
  onChange,
}: {
  label: string;
  placeholder?: string;
  values: string[];
  onChange: (next: string[]) => void;
}) {
  const [draft, setDraft] = useState("");
  const commit = () => {
    const clean = draft.trim();
    if (!clean) return;
    if (!values.includes(clean)) onChange([...values, clean]);
    setDraft("");
  };
  return (
    <div>
      <Label>{label}</Label>
      <div className="mt-1 flex flex-wrap items-center gap-1.5 rounded-md border px-2 py-2">
        {values.map((v, i) => (
          <Badge key={`${v}-${i}`} variant="secondary" className="gap-1">
            {v}
            <button
              type="button"
              className="ml-1 rounded-sm hover:bg-destructive/20"
              onClick={() => onChange(values.filter((_, idx) => idx !== i))}
              aria-label={`Remove ${v}`}
            >
              <X className="h-3 w-3" />
            </button>
          </Badge>
        ))}
        <Input
          placeholder={placeholder}
          value={draft}
          onChange={(e) => setDraft(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter" || e.key === ",") {
              e.preventDefault();
              commit();
            } else if (e.key === "Backspace" && !draft && values.length) {
              onChange(values.slice(0, -1));
            }
          }}
          onBlur={commit}
          className="flex-1 border-0 shadow-none focus-visible:ring-0 h-8 min-w-[8rem] px-1"
        />
      </div>
    </div>
  );
}
