import { useEffect, useRef, useState } from "react";
import { createFileRoute, Link } from "@tanstack/react-router";
import {
  useMutation,
  useQuery,
  useQueryClient,
} from "@tanstack/react-query";
import {
  ArrowLeft,
  AlertTriangle,
  Copy as CopyIcon,
  Plus,
} from "lucide-react";
import { toast } from "sonner";

import type { Plan, PlanStep, RelanceTone } from "@rubis/shared";
import { api } from "@/lib/api";
import { queryKeys } from "@/lib/queryKeys";
import {
  TEMPLATE_VARIABLES,
  TONE_LABELS,
  planMoodLabel,
} from "@/lib/plans";
import { cn } from "@/lib/utils";

import { Button } from "@/components/ui/Button";
import { Card } from "@/components/ui/Card";
import { Eyebrow } from "@/components/ui/Eyebrow";
import { Field } from "@/components/ui/Field";
import { Input } from "@/components/ui/Input";
import { Textarea } from "@/components/ui/Textarea";

type PlanWithUsage = Plan & { usageCount: number };

export const Route = createFileRoute("/_app/plans_/$slug")({
  component: PlanEditorPage,
  loader: ({ context, params }) => {
    void context.queryClient.prefetchQuery({
      queryKey: queryKeys.plans.detail(params.slug),
      queryFn: () => api.get<PlanWithUsage>(`/api/v1/plans/${params.slug}`),
    });
  },
});

function PlanEditorPage() {
  const { slug } = Route.useParams();
  const queryClient = useQueryClient();

  const { data: plan, isPending, isError } = useQuery({
    queryKey: queryKeys.plans.detail(slug),
    queryFn: () => api.get<PlanWithUsage>(`/api/v1/plans/${slug}`),
  });

  // État local de l'édition. On clone les steps depuis le serveur au mount
  // (et à chaque refetch) puis on travaille en local jusqu'au "Enregistrer".
  const [draftSteps, setDraftSteps] = useState<PlanStep[] | null>(null);
  const [selectedStepId, setSelectedStepId] = useState<string | null>(null);
  const bodyRef = useRef<HTMLTextAreaElement | null>(null);

  // Sync : quand le plan arrive ou change côté serveur, on remet à zéro l'état
  // local. On évite les races avec une clé sur plan.id+updatedAt.
  useEffect(() => {
    if (!plan) return;
    setDraftSteps(plan.steps);
    setSelectedStepId((current) => current ?? plan.steps[0]?.id ?? null);
  }, [plan?.id, plan?.updatedAt]); // eslint-disable-line react-hooks/exhaustive-deps

  const saveMutation = useMutation({
    mutationFn: (steps: PlanStep[]) =>
      api.patch<Plan>(`/api/v1/plans/${slug}`, { steps }),
    onSuccess: (saved) => {
      void queryClient.invalidateQueries({ queryKey: queryKeys.plans.all() });
      void queryClient.setQueryData(
        queryKeys.plans.detail(slug),
        (prev: PlanWithUsage | undefined) =>
          prev ? { ...prev, ...saved } : (saved as PlanWithUsage),
      );
      toast.success("Plan enregistré.");
    },
    onError: () => {
      toast.error("Sauvegarde impossible. Réessayez dans un instant.");
    },
  });

  if (isError) {
    return (
      <div className="text-center py-12">
        <p className="font-display text-[20px] font-semibold text-ink">
          Plan introuvable.
        </p>
        <Link
          to="/plans"
          className="mt-3 inline-block text-[13px] text-rubis underline-offset-4 hover:underline"
        >
          ← Retour à la bibliothèque
        </Link>
      </div>
    );
  }

  if (isPending || !plan || !draftSteps) {
    return <EditorSkeleton />;
  }

  const selectedStep =
    draftSteps.find((s) => s.id === selectedStepId) ?? draftSteps[0];

  const updateSelectedStep = (patch: Partial<PlanStep>) => {
    if (!selectedStep) return;
    setDraftSteps((prev) =>
      prev
        ? prev.map((s) => (s.id === selectedStep.id ? { ...s, ...patch } : s))
        : prev,
    );
  };

  const insertVariable = (token: string) => {
    if (!selectedStep || !bodyRef.current) return;
    const ta = bodyRef.current;
    const start = ta.selectionStart ?? selectedStep.body.length;
    const end = ta.selectionEnd ?? selectedStep.body.length;
    const newBody =
      selectedStep.body.slice(0, start) + token + selectedStep.body.slice(end);
    updateSelectedStep({ body: newBody });
    // Reposition le curseur après le token inséré
    requestAnimationFrame(() => {
      ta.focus();
      const cursor = start + token.length;
      ta.setSelectionRange(cursor, cursor);
    });
  };

  const isDirty =
    JSON.stringify(draftSteps) !== JSON.stringify(plan.steps);
  const mood = planMoodLabel({ steps: draftSteps });

  return (
    <div className="flex flex-col gap-6">
      <Link
        to="/plans"
        className="inline-flex items-center gap-1.5 self-start text-[12.5px] text-ink-3 hover:text-rubis"
      >
        <ArrowLeft size={13} aria-hidden="true" /> Plans · {plan.name}
      </Link>

      <header className="flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
        <div className="min-w-0">
          <Eyebrow>{mood}</Eyebrow>
          <h1 className="mt-2 font-display text-[28px] font-bold tracking-[-0.022em] text-ink lg:text-[32px]">
            {plan.name}
          </h1>
          <p className="mt-1.5 text-[13px] text-ink-3">
            {plan.usageCount > 0 ? (
              <>
                Utilisé sur{" "}
                <strong className="font-semibold text-ink-2 tabular-nums">
                  {plan.usageCount}
                </strong>{" "}
                facture{plan.usageCount > 1 ? "s" : ""} active
                {plan.usageCount > 1 ? "s" : ""}
              </>
            ) : (
              <span className="italic">Aucune facture active</span>
            )}
          </p>
        </div>

        <div className="flex flex-wrap gap-2">
          <Button variant="secondary" size="sm" disabled>
            <CopyIcon size={14} aria-hidden="true" /> Dupliquer
          </Button>
          <Button
            size="sm"
            disabled={!isDirty}
            loading={saveMutation.isPending}
            onClick={() => saveMutation.mutate(draftSteps)}
          >
            {isDirty ? "Enregistrer" : "Aucune modification"}
          </Button>
        </div>
      </header>

      <div className="grid grid-cols-1 gap-5 lg:grid-cols-[1fr_1.4fr]">
        {/* === Cadence (left) === */}
        <section aria-label="Cadence du plan" className="flex flex-col gap-3">
          <Eyebrow tone="ink">Cadence</Eyebrow>
          <ol className="flex flex-col gap-3">
            {draftSteps.map((step, idx) => (
              <li key={step.id}>
                <StepCard
                  step={step}
                  index={idx}
                  selected={step.id === selectedStep?.id}
                  onSelect={() => setSelectedStepId(step.id)}
                />
              </li>
            ))}
          </ol>
          <button
            type="button"
            disabled
            className={cn(
              "rounded-default border border-dashed border-line bg-transparent",
              "px-4 py-3 text-[13px] font-medium text-ink-3",
              "transition-colors hover:border-ink-3 hover:text-ink-2",
              "disabled:opacity-60 disabled:cursor-not-allowed",
              "inline-flex items-center justify-center gap-1.5",
            )}
          >
            <Plus size={14} aria-hidden="true" /> Ajouter une étape
            <span className="ml-1 text-[11px] italic">(bientôt)</span>
          </button>
        </section>

        {/* === Email editor (right) === */}
        <section aria-label="Édition de l'étape sélectionnée">
          {selectedStep ? (
            <Card padding="md" className="flex flex-col gap-5">
              <div className="flex items-baseline justify-between gap-3">
                <Eyebrow tone="ink">
                  Étape {(selectedStep.order ?? 0) + 1} ·{" "}
                  <span className="text-rubis">
                    J{selectedStep.offsetDays >= 0 ? "+" : ""}
                    {selectedStep.offsetDays}
                  </span>
                </Eyebrow>
                <span className="text-[12px] text-ink-3">
                  Tonalité · {TONE_LABELS[selectedStep.tone]}
                </span>
              </div>

              {selectedStep.requiresManualValidation && (
                <div className="flex items-start gap-2 rounded-default bg-rubis-glow/60 px-3 py-2 text-[12.5px] text-rubis-deep">
                  <AlertTriangle size={14} className="mt-0.5 shrink-0" aria-hidden="true" />
                  <p>
                    <strong className="font-semibold">Validation manuelle obligatoire.</strong>{" "}
                    L&apos;email est <em className="not-italic font-medium">généré en brouillon</em>,
                    jamais envoyé automatiquement (cf. principe produit).
                  </p>
                </div>
              )}

              <Field label="Objet de l'email" htmlFor={`subject-${selectedStep.id}`}>
                <Input
                  id={`subject-${selectedStep.id}`}
                  value={selectedStep.subject}
                  onChange={(e) => updateSelectedStep({ subject: e.target.value })}
                  placeholder="Rappel — facture {{numero}}"
                />
              </Field>

              <Field
                label="Corps"
                htmlFor={`body-${selectedStep.id}`}
                hint="Markdown léger non interprété. Les variables ci-dessous sont remplacées à l'envoi."
              >
                <Textarea
                  id={`body-${selectedStep.id}`}
                  ref={bodyRef}
                  rows={10}
                  className="font-mono text-[13px] leading-relaxed"
                  value={selectedStep.body}
                  onChange={(e) => updateSelectedStep({ body: e.target.value })}
                />
              </Field>

              <div>
                <p className="mb-2 text-[11px] font-semibold uppercase tracking-[0.12em] text-ink-3">
                  Variables — clic pour insérer au curseur
                </p>
                <div className="flex flex-wrap gap-2">
                  {TEMPLATE_VARIABLES.map((variable) => (
                    <button
                      key={variable.token}
                      type="button"
                      onClick={() => insertVariable(variable.token)}
                      className={cn(
                        "inline-flex items-center gap-1.5 rounded-full border px-3 py-1",
                        "font-mono text-[11.5px] leading-tight",
                        "border-line bg-cream-2 text-ink-2",
                        "transition-colors hover:border-rubis hover:bg-rubis-glow hover:text-rubis-deep",
                        "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-rubis-glow",
                      )}
                      title={`Insère "${variable.token}" — ex: ${variable.preview.split("\n")[0]}`}
                    >
                      <span className="font-sans text-[11px] text-ink-3 not-italic">
                        {variable.label}
                      </span>
                      <span className="text-rubis">{variable.token}</span>
                    </button>
                  ))}
                </div>
              </div>
            </Card>
          ) : (
            <Card padding="md">
              <p className="text-[13px] italic text-ink-3">
                Sélectionnez une étape à gauche pour l&apos;éditer.
              </p>
            </Card>
          )}
        </section>
      </div>
    </div>
  );
}

/** Card d'une étape dans la colonne cadence (gauche). */
function StepCard({
  step,
  index,
  selected,
  onSelect,
}: {
  step: PlanStep;
  index: number;
  selected: boolean;
  onSelect: () => void;
}) {
  return (
    <button
      type="button"
      aria-pressed={selected}
      onClick={onSelect}
      className={cn(
        "group block w-full text-left",
        "rounded-card border bg-white p-4 transition-all duration-150",
        selected
          ? "border-rubis shadow-rubis"
          : "border-line hover:border-ink-3",
        "focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-rubis-glow",
      )}
    >
      <span
        aria-hidden="true"
        className={cn(
          "absolute left-0 top-4 bottom-4 w-[3px] rounded-r-full transition-colors",
          selected ? "bg-rubis" : "bg-transparent",
        )}
      />
      <div className="flex items-baseline justify-between gap-3">
        <p className="font-sans text-[13.5px] font-semibold text-ink">
          Étape {index + 1} ·{" "}
          <span className="tabular-nums text-rubis">
            J{step.offsetDays >= 0 ? "+" : ""}
            {step.offsetDays}
          </span>
        </p>
        <span
          className={cn(
            "text-[11px] font-semibold uppercase tracking-[0.1em]",
            selected ? "text-rubis" : "text-ink-3",
          )}
        >
          {step.requiresManualValidation ? "Brouillon" : "Email"}
        </span>
      </div>
      <p className="mt-1.5 text-[12.5px] text-ink-2 line-clamp-2">
        « {step.subject} »
      </p>
      <p className="mt-1 text-[11px] text-ink-3">
        Tonalité · {TONE_LABELS[step.tone as RelanceTone]}
      </p>
    </button>
  );
}

function EditorSkeleton() {
  return (
    <div className="flex flex-col gap-6 animate-pulse">
      <div className="h-3 w-24 rounded bg-cream-2" />
      <div className="h-8 w-1/3 rounded bg-cream-2" />
      <div className="grid grid-cols-1 gap-5 lg:grid-cols-[1fr_1.4fr]">
        <div className="space-y-3">
          <div className="h-24 rounded-card bg-cream-2" />
          <div className="h-24 rounded-card bg-cream-2" />
          <div className="h-24 rounded-card bg-cream-2" />
        </div>
        <div className="h-96 rounded-card bg-cream-2" />
      </div>
    </div>
  );
}
