// StarterAccountsModal — one-shot opt-in modal proposing 4 starter accounts
// (Compte chèque, CELI, REER, Compte non-enregistré) to existing profiles
// when they first land on /balance. Issue #179.
//
// Behavior:
//   - 4 checkboxes default-checked.
//   - Collision rule (case-insensitive trim name + same category): the
//     matching checkbox is disabled and uncheckable; tooltip explains why.
//   - "Ajouter les comptes sélectionnés" → atomic BEGIN/COMMIT INSERT, then
//     onClose(insertedIds).
//   - "Plus tard" → no INSERT, onClose([]).
//   - Parent owns isOpen state and writes user_preferences.balance_starter_proposed
//     in onClose so the modal never re-appears.

import { useEffect, useState } from "react";
import { createPortal } from "react-dom";
import { useTranslation } from "react-i18next";
import { X, Loader2 } from "lucide-react";
import {
  STARTER_ACCOUNTS,
  getStarterCollisions,
  proposeStarterAccounts,
} from "../../services/balance.service";

export interface StarterAccountsModalProps {
  /** Parent guard — modal renders only when true. */
  isOpen: boolean;
  /**
   * Fired in both branches (confirm + dismiss). The parent uses the returned
   * ids to write `user_preferences.balance_starter_proposed` so the modal
   * never re-appears, regardless of which branch was taken.
   */
  onClose: (acceptedIds: number[]) => void;
}

export default function StarterAccountsModal({
  isOpen,
  onClose,
}: StarterAccountsModalProps) {
  const { t } = useTranslation();
  const [collisions, setCollisions] = useState<Set<string>>(new Set());
  const [selected, setSelected] = useState<Set<string>>(
    () => new Set(STARTER_ACCOUNTS.map((s) => s.key))
  );
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [collisionsLoaded, setCollisionsLoaded] = useState(false);

  // Load collisions once when the modal opens. We pre-uncheck colliding
  // starters (and disable them) so the visible default-checked count matches
  // what would actually be inserted.
  useEffect(() => {
    if (!isOpen) return;
    let cancelled = false;
    void (async () => {
      try {
        const c = await getStarterCollisions();
        if (cancelled) return;
        setCollisions(c);
        setSelected((prev) => {
          const next = new Set(prev);
          for (const k of c) next.delete(k);
          return next;
        });
        setCollisionsLoaded(true);
      } catch {
        if (!cancelled) setCollisionsLoaded(true);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [isOpen]);

  if (!isOpen) return null;

  const toggle = (key: string) => {
    if (collisions.has(key)) return;
    setSelected((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  };

  const handleAdd = async () => {
    if (submitting) return;
    setError(null);
    setSubmitting(true);
    try {
      const ids = await proposeStarterAccounts(Array.from(selected));
      setSubmitting(false);
      onClose(ids);
    } catch {
      setSubmitting(false);
      setError(t("balance.starters.errors.insert"));
    }
  };

  const handleLater = () => {
    if (submitting) return;
    onClose([]);
  };

  return createPortal(
    <div
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
      role="dialog"
      aria-modal="true"
      aria-labelledby="balance-starters-title"
      data-testid="balance-starters-modal"
    >
      <div className="bg-[var(--card)] rounded-xl border border-[var(--border)] shadow-xl max-w-md w-full">
        <div className="flex items-start justify-between p-5 border-b border-[var(--border)]">
          <div>
            <h2
              id="balance-starters-title"
              className="text-lg font-semibold"
            >
              {t("balance.starters.title")}
            </h2>
            <p className="text-sm text-[var(--muted-foreground)] mt-1">
              {t("balance.starters.description")}
            </p>
          </div>
          <button
            type="button"
            onClick={handleLater}
            aria-label={t("common.close")}
            className="text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
          >
            <X size={18} />
          </button>
        </div>

        <ul className="p-5 space-y-2" data-testid="balance-starters-list">
          {STARTER_ACCOUNTS.map((s) => {
            const isCollision = collisions.has(s.key);
            const isChecked = selected.has(s.key);
            return (
              <li key={s.key}>
                <label
                  className={`flex items-center gap-3 p-3 rounded-lg border ${
                    isCollision
                      ? "border-[var(--border)] opacity-60 cursor-not-allowed"
                      : "border-[var(--border)] hover:bg-[var(--muted)]/30 cursor-pointer"
                  }`}
                  title={
                    isCollision
                      ? t("balance.starters.collision_tooltip")
                      : undefined
                  }
                  data-testid={`balance-starter-row-${s.key}`}
                  data-collision={isCollision ? "true" : "false"}
                >
                  <input
                    type="checkbox"
                    checked={isChecked}
                    disabled={isCollision || submitting}
                    onChange={() => toggle(s.key)}
                    data-testid={`balance-starter-checkbox-${s.key}`}
                  />
                  <span className="text-sm font-medium">
                    {t(s.i18nKey)}
                  </span>
                  {isCollision && (
                    <span className="ml-auto text-xs italic text-[var(--muted-foreground)]">
                      {t("balance.starters.collision_tooltip")}
                    </span>
                  )}
                </label>
              </li>
            );
          })}
        </ul>

        {error && (
          <div className="mx-5 mb-3 p-2 rounded text-sm bg-[var(--negative)]/10 text-[var(--negative)] border border-[var(--negative)]/20">
            {error}
          </div>
        )}

        <div className="flex items-center justify-end gap-2 p-5 border-t border-[var(--border)]">
          <button
            type="button"
            onClick={handleLater}
            disabled={submitting}
            data-testid="balance-starters-cta-later"
            className="px-4 py-2 rounded-lg border border-[var(--border)] text-sm font-medium hover:bg-[var(--muted)]/30 disabled:opacity-50"
          >
            {t("balance.starters.cta_later")}
          </button>
          <button
            type="button"
            onClick={handleAdd}
            disabled={submitting || !collisionsLoaded || selected.size === 0}
            data-testid="balance-starters-cta-add"
            className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-[var(--primary)] text-white text-sm font-medium hover:opacity-90 disabled:opacity-50"
          >
            {submitting && <Loader2 size={14} className="animate-spin" />}
            {t("balance.starters.cta_add")}
          </button>
        </div>
      </div>
    </div>,
    document.body
  );
}
