import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { ShieldCheck, X, RotateCcw } from "lucide-react";
import {
  CATEGORIES_MIGRATION_BANNER_DISMISSED_KEY,
  readLastMigrationJournal,
  shouldShowBanner,
  type RestorableMigrationJournal,
} from "../../services/categoryRestoreService";
import { getPreference, setPreference } from "../../services/userPreferenceService";
import CategoriesMigrationRestoreModal from "./CategoriesMigrationRestoreModal";

// -----------------------------------------------------------------------------
// Post-migration backup banner (shown in Settings > Categories for 90 days).
//
// Decides visibility client-side using the `shouldShowBanner` pure helper and
// three prefs:
//   - `last_categories_migration` JSON (written by categoryMigrationService).
//   - `categories_migration_banner_dismissed` (flag, "1" = dismissed).
//   - `reverted_at` (merged into the journal once a restore succeeds).
//
// The banner never surfaces the restore action directly — it opens a confirm
// modal that enforces a two-step consent before the destructive wipe.
// -----------------------------------------------------------------------------

type Visibility = "loading" | "visible" | "hidden";

export default function CategoriesMigrationBackupBanner() {
  const { t } = useTranslation();
  const [visibility, setVisibility] = useState<Visibility>("loading");
  const [journal, setJournal] = useState<RestorableMigrationJournal | null>(
    null,
  );
  const [isModalOpen, setIsModalOpen] = useState(false);

  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const [loadedJournal, dismissed] = await Promise.all([
          readLastMigrationJournal(),
          getPreference(CATEGORIES_MIGRATION_BANNER_DISMISSED_KEY),
        ]);
        if (cancelled) return;
        setJournal(loadedJournal);
        setVisibility(
          shouldShowBanner(loadedJournal, dismissed) ? "visible" : "hidden",
        );
      } catch {
        if (!cancelled) setVisibility("hidden");
      }
    })();
    return () => {
      cancelled = true;
    };
  }, []);

  async function dismiss() {
    // Optimistically hide, then persist. Matches the pattern used in
    // CategoriesV1DiscoveryBanner — a failed write just means the banner will
    // reappear on next launch, which is an acceptable degradation.
    setVisibility("hidden");
    try {
      await setPreference(CATEGORIES_MIGRATION_BANNER_DISMISSED_KEY, "1");
    } catch {
      // Swallow — see comment above.
    }
  }

  if (visibility !== "visible" || !journal) return null;

  // Compute the 90-day deadline for display. Falls back to the raw timestamp
  // if the journal date cannot be parsed (the banner is still useful in that
  // edge case — the CTA does not rely on this value).
  const expiryLabel = formatExpiry(journal.timestamp);

  return (
    <>
      <div
        role="status"
        className="flex items-start gap-3 rounded-xl border border-[var(--primary)]/30 bg-[var(--primary)]/5 p-4"
      >
        <div className="mt-0.5 shrink-0 rounded-lg bg-[var(--primary)]/10 p-2 text-[var(--primary)]">
          <ShieldCheck size={18} />
        </div>
        <div className="flex-1 space-y-2 text-sm">
          <p className="font-semibold text-[var(--foreground)]">
            {t("settings.categoriesCard.restoreBanner.title")}
          </p>
          <p className="text-[var(--muted-foreground)]">
            {t("settings.categoriesCard.restoreBanner.description", {
              expiry: expiryLabel,
            })}
          </p>
          <button
            type="button"
            onClick={() => setIsModalOpen(true)}
            className="inline-flex items-center gap-1.5 font-medium text-[var(--primary)] hover:underline"
          >
            <RotateCcw size={14} />
            {t("settings.categoriesCard.restoreBanner.cta")}
          </button>
        </div>
        <button
          type="button"
          onClick={dismiss}
          aria-label={t("settings.categoriesCard.restoreBanner.dismiss")}
          className="shrink-0 rounded-md p-1 text-[var(--muted-foreground)] hover:bg-[var(--muted)] hover:text-[var(--foreground)] transition-colors"
        >
          <X size={16} />
        </button>
      </div>

      {isModalOpen && (
        <CategoriesMigrationRestoreModal
          journal={journal}
          onClose={() => setIsModalOpen(false)}
        />
      )}
    </>
  );
}

/**
 * Produce an end-of-window label: backup timestamp + 90 days, formatted in
 * the browser locale. Returns the raw journal timestamp on parse failure.
 */
function formatExpiry(journalTimestamp: string): string {
  const ts = Date.parse(journalTimestamp);
  if (Number.isNaN(ts)) return journalTimestamp;
  const deadline = new Date(ts + 90 * 24 * 60 * 60 * 1000);
  return deadline.toLocaleDateString();
}
