import { useEffect, useMemo, useState } from "react";
import {
  ChevronLeft,
  ChevronRight,
  Plus,
  Trash2,
  AlertTriangle,
  Smile,
  MessageCircle,
  Gavel,
  type LucideIcon,
} from "lucide-react";
import { toast } from "sonner";
import type { RelanceTone } from "@rubis/shared";

import { cn } from "@/lib/utils";
import { TONE_LABELS } from "@/lib/plans";

/**
 * Icône qui illustre chaque tonalité. Cohérent avec l'escalade émotionnelle :
 * sourire pour amical, bulle de conversation pour standard, alerte pour
 * ferme, marteau de juge pour mise en demeure.
 */
const TONE_ICON: Record<RelanceTone, LucideIcon> = {
  amical: Smile,
  courtois: MessageCircle,
  ferme: AlertTriangle,
  mise_en_demeure: Gavel,
};

/**
 * Étape minimale pour la visu calendrier (subject/body arrivent à
 * l'étape 3 du wizard).
 */
export type DraftStepLite = {
  offsetDays: number;
  tone: RelanceTone;
  requiresManualValidation?: boolean;
};

/**
 * Mini-calendrier mensuel pratique : on **navigue** dans la cadence,
 * on **ajoute** une relance en touchant une case vide, on **modifie**
 * en touchant une case colorée. Édition compacte juste en dessous.
 *
 * Distinctions visuelles importantes :
 *  - Échéance : ◆ rubis solide (le repère héros, pas une "tonalité")
 *  - Étape : couleur de fond selon la tonalité (4 niveaux escalade)
 *  - Sélectionnée : ring rubis + scale
 *  - Vide : numéro discret, hover montre que c'est cliquable
 *
 * Date de référence : le 15 du mois prochain.
 */
export function CadenceCalendar({
  steps,
  selectedIndex = -1,
  onSelectStep,
  onUpdateStep,
  onAddStep,
  onAddStepAtOffset,
  onRemoveStep,
}: {
  steps: DraftStepLite[];
  selectedIndex?: number;
  onSelectStep?: (idx: number) => void;
  onUpdateStep?: (idx: number, patch: Partial<DraftStepLite>) => void;
  onAddStep: () => void;
  /** Création directe en cliquant une case vide. Reçoit l'offset depuis
   *  l'échéance. La tonalité par défaut est gérée côté parent. */
  onAddStepAtOffset?: (offsetDays: number) => void;
  onRemoveStep: (idx: number) => void;
}) {
  const dueDate = useMemo(() => {
    const now = new Date();
    return new Date(now.getFullYear(), now.getMonth() + 1, 15);
  }, []);

  const stepDates = useMemo(
    () => steps.map((s) => addDays(dueDate, s.offsetDays)),
    [steps, dueDate],
  );

  const [viewMonth, setViewMonth] = useState<Date>(
    () => new Date(dueDate.getFullYear(), dueDate.getMonth(), 1),
  );

  // Auto-jump au mois de l'étape sélectionnée pour ne pas perdre la cellule.
  useEffect(() => {
    if (selectedIndex < 0) return;
    const target = stepDates[selectedIndex];
    if (!target) return;
    setViewMonth((cur) => {
      if (cur.getFullYear() === target.getFullYear() && cur.getMonth() === target.getMonth()) {
        return cur;
      }
      return new Date(target.getFullYear(), target.getMonth(), 1);
    });
  }, [selectedIndex, stepDates]);

  const selected = selectedIndex >= 0 ? steps[selectedIndex] : null;

  const handleEmptyDayClick = (date: Date) => {
    if (steps.length >= 8) {
      toast.info("8 étapes max par plan.");
      return;
    }
    const offset = daysBetween(dueDate, date);
    if (offset < -30 || offset > 180) {
      toast.info("Hors plage : on accepte de J-30 à J+180.");
      return;
    }
    onAddStepAtOffset?.(offset);
  };

  return (
    <div className="rounded-card border border-line bg-cream/40 p-3 sm:p-4 mx-auto max-w-md">
      {/* Header — month nav */}
      <div className="flex items-center justify-between gap-2 mb-2">
        <button
          type="button"
          onClick={() =>
            setViewMonth((d) => new Date(d.getFullYear(), d.getMonth() - 1, 1))
          }
          className="size-8 flex items-center justify-center rounded-default text-ink-2 hover:bg-cream-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow"
          aria-label="Mois précédent"
        >
          <ChevronLeft size={16} />
        </button>
        <p className="font-display text-[14px] font-semibold text-ink capitalize tabular-nums">
          {formatMonth(viewMonth)}
        </p>
        <button
          type="button"
          onClick={() =>
            setViewMonth((d) => new Date(d.getFullYear(), d.getMonth() + 1, 1))
          }
          className="size-8 flex items-center justify-center rounded-default text-ink-2 hover:bg-cream-2 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow"
          aria-label="Mois suivant"
        >
          <ChevronRight size={16} />
        </button>
      </div>

      <MonthGrid
        month={viewMonth}
        dueDate={dueDate}
        steps={steps}
        stepDates={stepDates}
        selectedIndex={selectedIndex}
        onSelectStep={onSelectStep}
        onClickDueDate={() =>
          toast.info(
            `Le 15 du mois est l'échéance fictive de votre facture. Elle ne se modifie pas — c'est juste un repère pour visualiser le timing.`,
          )
        }
        onClickEmptyDay={handleEmptyDayClick}
        canAddMore={steps.length < 8}
      />

      {/* Légende compacte avec les 4 tonalités + échéance */}
      <div className="mt-3 flex flex-wrap items-center gap-x-3 gap-y-1 text-[10.5px] text-ink-3">
        <LegendDot
          label="Échéance"
          className="bg-rubis border-rubis"
          isDiamond
        />
        <LegendDot label="Doux" className="bg-rubis-glow border-rubis" />
        <LegendDot label="Standard" className="bg-cream-2 border-ink-3" />
        <LegendDot label="Ferme" className="bg-ink border-ink" />
        <LegendDot label="Strict" className="bg-rubis-deep border-rubis-deep" />
      </div>

      {/* Éditeur ou hint */}
      <div className="mt-3 pt-3 border-t border-line">
        {selected ? (
          <CompactEditor
            step={selected}
            stepDate={stepDates[selectedIndex]!}
            canRemove={steps.length > 1}
            onUpdate={(patch) => onUpdateStep?.(selectedIndex, patch)}
            onRemove={() => onRemoveStep(selectedIndex)}
          />
        ) : (
          <p className="text-[12.5px] text-ink-3 italic text-center py-2 leading-snug">
            Touchez une <strong className="text-ink-2">case colorée</strong> pour
            modifier une relance, ou un <strong className="text-ink-2">jour vide</strong>{" "}
            pour en ajouter une.
          </p>
        )}
      </div>

      {/* Footer — bouton Ajouter de secours */}
      {steps.length < 8 && (
        <button
          type="button"
          onClick={onAddStep}
          className={cn(
            "mt-3 w-full inline-flex items-center justify-center gap-1.5",
            "rounded-default border border-dashed border-line bg-transparent",
            "px-3 py-2 text-[12.5px] font-medium text-ink-3",
            "hover:border-rubis hover:text-rubis-deep hover:bg-rubis-glow/20 transition-colors",
            "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow",
          )}
        >
          <Plus size={13} aria-hidden="true" /> Ajouter en bout de cadence
        </button>
      )}
    </div>
  );
}

function LegendDot({
  label,
  className,
  isDiamond = false,
}: {
  label: string;
  className: string;
  isDiamond?: boolean;
}) {
  return (
    <span className="inline-flex items-center gap-1">
      <span
        aria-hidden="true"
        className={cn(
          "size-2.5 border",
          isDiamond ? "rotate-45" : "rounded-full",
          className,
        )}
      />
      {label}
    </span>
  );
}

// ============================================================================
// Grille mensuelle — cellules carrées (aspect-square), max-w via container
// ============================================================================

const TONE_CELL_CLASS: Record<RelanceTone, string> = {
  amical: "bg-rubis-glow border-rubis text-rubis-deep",
  courtois: "bg-cream-2 border-ink-3 text-ink",
  ferme: "bg-ink border-ink text-cream",
  mise_en_demeure: "bg-rubis-deep border-rubis-deep text-cream",
};

const WEEKDAY_LABELS = ["L", "M", "M", "J", "V", "S", "D"];

function MonthGrid({
  month,
  dueDate,
  steps,
  stepDates,
  selectedIndex,
  onSelectStep,
  onClickDueDate,
  onClickEmptyDay,
  canAddMore,
}: {
  month: Date;
  dueDate: Date;
  steps: DraftStepLite[];
  stepDates: Date[];
  selectedIndex: number;
  onSelectStep?: (idx: number) => void;
  onClickDueDate: () => void;
  onClickEmptyDay: (date: Date) => void;
  canAddMore: boolean;
}) {
  const firstDay = new Date(month.getFullYear(), month.getMonth(), 1);
  const lastDay = new Date(month.getFullYear(), month.getMonth() + 1, 0);
  const firstDayOfWeek = (firstDay.getDay() + 6) % 7;

  const cells: (Date | null)[] = [];
  for (let i = 0; i < firstDayOfWeek; i++) cells.push(null);
  for (let d = 1; d <= lastDay.getDate(); d++) {
    cells.push(new Date(month.getFullYear(), month.getMonth(), d));
  }
  while (cells.length % 7 !== 0) cells.push(null);

  return (
    <div className="grid grid-cols-7 gap-1">
      {WEEKDAY_LABELS.map((label, i) => (
        <div
          key={`wd-${i}`}
          className="text-center text-[10px] font-semibold uppercase tracking-wide text-ink-3 pb-0.5"
        >
          {label}
        </div>
      ))}
      {cells.map((date, i) => (
        <DayCell
          key={i}
          date={date}
          dueDate={dueDate}
          steps={steps}
          stepDates={stepDates}
          selectedIndex={selectedIndex}
          onSelectStep={onSelectStep}
          onClickDueDate={onClickDueDate}
          onClickEmptyDay={onClickEmptyDay}
          canAddMore={canAddMore}
        />
      ))}
    </div>
  );
}

function DayCell({
  date,
  dueDate,
  steps,
  stepDates,
  selectedIndex,
  onSelectStep,
  onClickDueDate,
  onClickEmptyDay,
  canAddMore,
}: {
  date: Date | null;
  dueDate: Date;
  steps: DraftStepLite[];
  stepDates: Date[];
  selectedIndex: number;
  onSelectStep?: (idx: number) => void;
  onClickDueDate: () => void;
  onClickEmptyDay: (date: Date) => void;
  canAddMore: boolean;
}) {
  if (!date) return <div aria-hidden="true" className="aspect-square" />;

  const isDue = sameDay(date, dueDate);
  const stepIdx = lastIndexOf(stepDates, (d) => sameDay(d, date));
  const step = stepIdx >= 0 ? steps[stepIdx] : null;
  const isSelected = stepIdx === selectedIndex;
  const isToday = sameDay(date, new Date());
  const offsetFromDue = daysBetween(dueDate, date);

  // Cellule échéance — solide rubis, ◆ visible, cliquable pour info
  if (isDue) {
    return (
      <button
        type="button"
        onClick={onClickDueDate}
        className={cn(
          "aspect-square flex flex-col items-center justify-center rounded-default",
          "border-2 border-rubis bg-rubis text-cream relative",
          "shadow-rubis hover:shadow-rubis-hover transition-shadow",
          "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow",
        )}
        aria-label={`Échéance fictive le ${formatLongDate(date)}`}
      >
        <span className="size-2.5 rotate-45 bg-cream" aria-hidden="true" />
        <span className="text-[11px] font-bold tabular-nums mt-0.5">
          {date.getDate()}
        </span>
      </button>
    );
  }

  // Cellule étape — couleur tonalité, badge "Jx" en coin, cliquable
  if (step) {
    return (
      <button
        type="button"
        onClick={() => onSelectStep?.(stepIdx)}
        aria-pressed={isSelected}
        aria-label={`${TONE_LABELS[step.tone]}, J${step.offsetDays >= 0 ? "+" : ""}${step.offsetDays}, le ${formatLongDate(date)}`}
        title={`${TONE_LABELS[step.tone]} · J${step.offsetDays >= 0 ? "+" : ""}${step.offsetDays}`}
        className={cn(
          "aspect-square flex flex-col items-center justify-center rounded-default",
          "border-2 transition-all relative",
          "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow",
          TONE_CELL_CLASS[step.tone],
          isSelected
            ? "ring-4 ring-rubis-glow scale-[1.08] z-10 shadow-rubis-hover"
            : "hover:scale-[1.04]",
        )}
      >
        <span className="absolute top-0.5 right-1 text-[8.5px] font-bold tabular-nums opacity-70">
          J{step.offsetDays >= 0 ? "+" : ""}
          {step.offsetDays}
        </span>
        <span className="text-[13px] font-bold tabular-nums">
          {date.getDate()}
        </span>
      </button>
    );
  }

  // Jour vide — cliquable pour ajouter une étape à cet offset (si dans la plage)
  const inRange = offsetFromDue >= -30 && offsetFromDue <= 180 && offsetFromDue !== 0;
  return (
    <button
      type="button"
      onClick={() => inRange && canAddMore && onClickEmptyDay(date)}
      disabled={!inRange || !canAddMore}
      aria-label={
        inRange && canAddMore
          ? `Ajouter une relance le ${formatLongDate(date)} (J${offsetFromDue >= 0 ? "+" : ""}${offsetFromDue})`
          : formatLongDate(date)
      }
      className={cn(
        "aspect-square flex items-center justify-center rounded-default group",
        "text-[12px] tabular-nums text-ink-3 transition-colors",
        isToday && "ring-1 ring-rubis-glow",
        inRange &&
          canAddMore &&
          "hover:bg-rubis-glow/40 hover:text-rubis-deep cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow",
        (!inRange || !canAddMore) && "cursor-default",
      )}
    >
      <span className="group-hover:hidden">{date.getDate()}</span>
      {inRange && canAddMore && (
        <Plus
          size={13}
          className="hidden group-hover:block text-rubis"
          aria-hidden="true"
        />
      )}
    </button>
  );
}

// ============================================================================
// Éditeur compact — header avec icône de tonalité + sélecteur 4 icônes
// ============================================================================

const TONE_ORDER: RelanceTone[] = [
  "amical",
  "courtois",
  "ferme",
  "mise_en_demeure",
];

function CompactEditor({
  step,
  stepDate,
  canRemove,
  onUpdate,
  onRemove,
}: {
  step: DraftStepLite;
  stepDate: Date;
  canRemove: boolean;
  onUpdate: (patch: Partial<DraftStepLite>) => void;
  onRemove: () => void;
}) {
  const HeaderIcon = TONE_ICON[step.tone];

  return (
    <div className="space-y-3">
      {/* Header : pastille tonalité (icône + couleur) + date + supprimer */}
      <div className="flex items-center justify-between gap-2">
        <div className="flex items-center gap-2 min-w-0">
          <span
            aria-hidden="true"
            className={cn(
              "shrink-0 flex items-center justify-center size-7 rounded-full border-2",
              TONE_CELL_CLASS[step.tone],
            )}
          >
            <HeaderIcon size={14} aria-hidden="true" />
          </span>
          <p className="text-[13px] text-ink-2 truncate leading-tight">
            Relance du{" "}
            <strong className="font-display font-semibold text-ink">
              {formatShortDate(stepDate)}
            </strong>{" "}
            <span className="text-ink-3">
              · J{step.offsetDays >= 0 ? "+" : ""}
              {step.offsetDays}
            </span>
          </p>
        </div>
        {canRemove && (
          <button
            type="button"
            onClick={onRemove}
            className="shrink-0 size-8 flex items-center justify-center rounded-default text-ink-3 hover:text-rubis-deep hover:bg-rubis-glow/40 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow"
            aria-label="Supprimer cette relance"
            title="Supprimer cette relance"
          >
            <Trash2 size={14} />
          </button>
        )}
      </div>

      {/* Sélecteur de tonalité : 4 boutons icône, plus visuel qu'un select */}
      <div>
        <p className="text-[11px] font-semibold text-ink-3 mb-1.5">Tonalité</p>
        <div className="grid grid-cols-4 gap-1.5">
          {TONE_ORDER.map((t) => {
            const Icon = TONE_ICON[t];
            const isActive = step.tone === t;
            return (
              <button
                key={t}
                type="button"
                onClick={() =>
                  onUpdate({
                    tone: t,
                    requiresManualValidation: t === "mise_en_demeure",
                  })
                }
                aria-pressed={isActive}
                aria-label={TONE_LABELS[t]}
                title={TONE_LABELS[t]}
                className={cn(
                  "flex flex-col items-center justify-center gap-0.5 py-2 px-1",
                  "rounded-default border-2 transition-all",
                  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-rubis-glow",
                  isActive
                    ? cn(TONE_CELL_CLASS[t], "ring-2 ring-rubis-glow scale-[1.04]")
                    : "border-line bg-white text-ink-2 hover:border-ink-3 hover:bg-cream-2/50",
                )}
              >
                <Icon size={16} aria-hidden="true" />
                <span className="text-[10.5px] font-semibold leading-tight">
                  {TONE_LABELS[t]}
                </span>
              </button>
            );
          })}
        </div>
      </div>

      {step.tone === "mise_en_demeure" && (
        <p className="inline-flex items-center gap-1.5 text-[11.5px] text-rubis-deep">
          <AlertTriangle size={11} /> Validation manuelle obligatoire avant envoi.
        </p>
      )}
    </div>
  );
}

// ============================================================================
// Helpers
// ============================================================================
function addDays(d: Date, n: number): Date {
  const r = new Date(d);
  r.setDate(r.getDate() + n);
  return r;
}

function sameDay(a: Date, b: Date): boolean {
  return (
    a.getFullYear() === b.getFullYear() &&
    a.getMonth() === b.getMonth() &&
    a.getDate() === b.getDate()
  );
}

function daysBetween(from: Date, to: Date): number {
  const a = new Date(from.getFullYear(), from.getMonth(), from.getDate()).getTime();
  const b = new Date(to.getFullYear(), to.getMonth(), to.getDate()).getTime();
  return Math.round((b - a) / 86400000);
}

function lastIndexOf<T>(arr: T[], pred: (x: T) => boolean): number {
  for (let i = arr.length - 1; i >= 0; i--) {
    if (pred(arr[i]!)) return i;
  }
  return -1;
}

const FR_MONTH = new Intl.DateTimeFormat("fr-FR", {
  month: "long",
  year: "numeric",
});

const FR_LONG = new Intl.DateTimeFormat("fr-FR", {
  weekday: "long",
  day: "numeric",
  month: "long",
});

const FR_SHORT = new Intl.DateTimeFormat("fr-FR", {
  day: "numeric",
  month: "long",
});

function formatMonth(d: Date): string {
  return FR_MONTH.format(d);
}

function formatLongDate(d: Date): string {
  return FR_LONG.format(d);
}

function formatShortDate(d: Date): string {
  return FR_SHORT.format(d);
}

