import {
  Check,
  Clipboard,
  FileDown,
  FileInput,
  Languages,
  Plus,
  RefreshCcw,
  Settings,
  ShieldCheck,
  SunMoon,
  Trash2,
  X
} from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import type { ReactNode } from "react";
import packageInfo from "../package.json";
import { transformEnv } from "./env";
import type { EnvDefault } from "./env";

type ThemeMode = "system" | "light" | "dark";
type Language = "de" | "en" | "es" | "fr" | "nl";
type DefaultSource = "auto" | "manual";

interface DefaultRow extends EnvDefault {
  source: DefaultSource;
  index?: number;
}

const initialDefaults: EnvDefault[] = [];

const languageNames: Record<Language, string> = {
  de: "Deutsch",
  en: "English",
  es: "Español",
  fr: "Français",
  nl: "Nederlands"
};

const translations = {
  de: {
    appSubtitle: "Lokaler Helfer für .env Vorlagen",
    loadFile: "Datei laden",
    saveFile: ".env speichern",
    settings: "Einstellungen",
    input: "Input",
    output: "Output",
    inputHint: "Datei laden oder Text direkt bearbeiten",
    outputHint: "Generierte .env, bereit zum Kopieren",
    copy: "Kopieren",
    copied: "Kopiert",
    regenerate: "Neu erzeugen",
    hits: "Fundstellen",
    values: "Werte",
    defaultsApplied: "Defaults",
    source: "Quelle",
    file: "Datei",
    text: "Text",
    defaults: "Standardwerte",
    defaultsHint: "Mögliche Standardwerte werden erkannt; eigene Werte überschreiben passende Keys im Output.",
    addDefault: "Standardwert hinzufügen",
    autoDefault: "Auto",
    manualDefault: "Manuell",
    key: "Key",
    value: "Wert",
    detected: "Erkannte Anforderungen",
    detectedHint: "Placeholder, betroffene Keys und erzeugtes Format.",
    placeholder: "Placeholder",
    keys: "Keys",
    format: "Format",
    noPlaceholders: "Keine CHANGE_ME Platzhalter gefunden.",
    language: "Sprache",
    theme: "Darstellung",
    system: "System",
    light: "Hell",
    dark: "Dunkel",
    version: "Version",
    author: "Autor",
    repository: "Repository"
  },
  en: {
    appSubtitle: "Local helper for .env templates",
    loadFile: "Load file",
    saveFile: "Save .env",
    settings: "Settings",
    input: "Input",
    output: "Output",
    inputHint: "Load a file or edit text directly",
    outputHint: "Generated .env, ready to copy",
    copy: "Copy",
    copied: "Copied",
    regenerate: "Regenerate",
    hits: "Matches",
    values: "Values",
    defaultsApplied: "Defaults",
    source: "Source",
    file: "File",
    text: "Text",
    defaults: "Defaults",
    defaultsHint: "Possible defaults are detected; custom values override matching keys in the output.",
    addDefault: "Add default",
    autoDefault: "Auto",
    manualDefault: "Manual",
    key: "Key",
    value: "Value",
    detected: "Detected requirements",
    detectedHint: "Placeholder, affected keys, and generated format.",
    placeholder: "Placeholder",
    keys: "Keys",
    format: "Format",
    noPlaceholders: "No CHANGE_ME placeholders found.",
    language: "Language",
    theme: "Appearance",
    system: "System",
    light: "Light",
    dark: "Dark",
    version: "Version",
    author: "Author",
    repository: "Repository"
  },
  es: {
    appSubtitle: "Ayudante local para plantillas .env",
    loadFile: "Cargar archivo",
    saveFile: "Guardar .env",
    settings: "Ajustes",
    input: "Entrada",
    output: "Salida",
    inputHint: "Carga un archivo o edita el texto",
    outputHint: ".env generado, listo para copiar",
    copy: "Copiar",
    copied: "Copiado",
    regenerate: "Regenerar",
    hits: "Coincidencias",
    values: "Valores",
    defaultsApplied: "Defaults",
    source: "Origen",
    file: "Archivo",
    text: "Texto",
    defaults: "Valores predeterminados",
    defaultsHint: "Se detectan valores posibles; los personalizados sobrescriben claves iguales.",
    addDefault: "Añadir valor",
    autoDefault: "Auto",
    manualDefault: "Manual",
    key: "Clave",
    value: "Valor",
    detected: "Requisitos detectados",
    detectedHint: "Placeholder, claves afectadas y formato generado.",
    placeholder: "Placeholder",
    keys: "Claves",
    format: "Formato",
    noPlaceholders: "No se encontraron placeholders CHANGE_ME.",
    language: "Idioma",
    theme: "Apariencia",
    system: "Sistema",
    light: "Claro",
    dark: "Oscuro",
    version: "Versión",
    author: "Autor",
    repository: "Repositorio"
  },
  fr: {
    appSubtitle: "Assistant local pour modèles .env",
    loadFile: "Charger",
    saveFile: "Enregistrer .env",
    settings: "Réglages",
    input: "Entrée",
    output: "Sortie",
    inputHint: "Chargez un fichier ou modifiez le texte",
    outputHint: ".env généré, prêt à copier",
    copy: "Copier",
    copied: "Copié",
    regenerate: "Régénérer",
    hits: "Occurrences",
    values: "Valeurs",
    defaultsApplied: "Défauts",
    source: "Source",
    file: "Fichier",
    text: "Texte",
    defaults: "Valeurs par défaut",
    defaultsHint: "Les valeurs possibles sont détectées; les valeurs personnalisées remplacent les clés.",
    addDefault: "Ajouter",
    autoDefault: "Auto",
    manualDefault: "Manuel",
    key: "Clé",
    value: "Valeur",
    detected: "Exigences détectées",
    detectedHint: "Placeholder, clés concernées et format généré.",
    placeholder: "Placeholder",
    keys: "Clés",
    format: "Format",
    noPlaceholders: "Aucun placeholder CHANGE_ME trouvé.",
    language: "Langue",
    theme: "Apparence",
    system: "Système",
    light: "Clair",
    dark: "Sombre",
    version: "Version",
    author: "Auteur",
    repository: "Dépôt"
  },
  nl: {
    appSubtitle: "Lokale helper voor .env sjablonen",
    loadFile: "Bestand laden",
    saveFile: ".env opslaan",
    settings: "Instellingen",
    input: "Input",
    output: "Output",
    inputHint: "Laad een bestand of bewerk tekst direct",
    outputHint: "Gegenereerde .env, klaar om te kopiëren",
    copy: "Kopiëren",
    copied: "Gekopieerd",
    regenerate: "Opnieuw maken",
    hits: "Treffers",
    values: "Waarden",
    defaultsApplied: "Defaults",
    source: "Bron",
    file: "Bestand",
    text: "Tekst",
    defaults: "Standaardwaarden",
    defaultsHint: "Mogelijke standaardwaarden worden herkend; eigen waarden overschrijven keys.",
    addDefault: "Waarde toevoegen",
    autoDefault: "Auto",
    manualDefault: "Handmatig",
    key: "Key",
    value: "Waarde",
    detected: "Gedetecteerde eisen",
    detectedHint: "Placeholder, betrokken keys en gegenereerd formaat.",
    placeholder: "Placeholder",
    keys: "Keys",
    format: "Formaat",
    noPlaceholders: "Geen CHANGE_ME placeholders gevonden.",
    language: "Taal",
    theme: "Weergave",
    system: "Systeem",
    light: "Licht",
    dark: "Donker",
    version: "Versie",
    author: "Auteur",
    repository: "Repository"
  }
} satisfies Record<Language, Record<string, string>>;

const themeLabels: ThemeMode[] = ["system", "light", "dark"];

function detectLanguage(): Language {
  const stored = localStorage.getItem("envhelper-language") as Language | null;
  if (stored && stored in languageNames) {
    return stored;
  }

  const browserLanguage = navigator.language.slice(0, 2) as Language;
  return browserLanguage in languageNames ? browserLanguage : "en";
}

function readDefaults(): EnvDefault[] {
  const stored = localStorage.getItem("envhelper-defaults");
  if (!stored) {
    return initialDefaults;
  }

  try {
    const parsed = JSON.parse(stored) as EnvDefault[];
    return Array.isArray(parsed) ? parsed.filter((entry) => entry.key && typeof entry.value === "string" && canPersistDefault(entry.key)) : initialDefaults;
  } catch {
    return initialDefaults;
  }
}

function canPersistDefault(key: string): boolean {
  const signal = normalizeKey(key);
  return !["PASSWORD", "SECRET", "TOKEN", "PRIVATE_KEY", "API_KEY", "ACCESS_KEY"].some((marker) => signal.includes(marker));
}

function readIgnoredAutoDefaults(): string[] {
  const stored = localStorage.getItem("envhelper-ignored-auto-defaults");
  if (!stored) {
    return [];
  }

  try {
    const parsed = JSON.parse(stored) as string[];
    return Array.isArray(parsed) ? parsed.filter(Boolean) : [];
  } catch {
    return [];
  }
}

function readTheme(): ThemeMode {
  const stored = localStorage.getItem("envhelper-theme") as ThemeMode | null;
  return stored && themeLabels.includes(stored) ? stored : "system";
}

function inferAutomaticDefaults(input: string, manualDefaults: EnvDefault[], ignoredKeys: string[]): EnvDefault[] {
  const manualKeys = new Set(manualDefaults.map((entry) => normalizeKey(entry.key)).filter(Boolean));
  const ignored = new Set(ignoredKeys.map(normalizeKey).filter(Boolean));
  const defaults = new Map<string, EnvDefault>();

  for (const line of input.split(/\r?\n/)) {
    const parsed = parseEnvAssignment(line);
    if (!parsed) {
      continue;
    }

    const normalizedKey = normalizeKey(parsed.key);
    if (!normalizedKey || manualKeys.has(normalizedKey) || ignored.has(normalizedKey)) {
      continue;
    }

    const value = inferDefaultValue(parsed.key, parsed.value);
    if (value) {
      defaults.set(normalizedKey, { key: parsed.key, value });
    }
  }

  return [...defaults.values()].sort((a, b) => a.key.localeCompare(b.key));
}

function parseEnvAssignment(line: string): { key: string; value: string } | null {
  const trimmed = line.trim();
  if (!trimmed || trimmed.startsWith("#")) {
    return null;
  }

  const normalized = trimmed.startsWith("export ") ? trimmed.slice(7).trimStart() : trimmed;
  const equalsIndex = normalized.indexOf("=");
  if (equalsIndex <= 0) {
    return null;
  }

  return {
    key: normalized.slice(0, equalsIndex).trim(),
    value: normalized.slice(equalsIndex + 1).trim()
  };
}

function inferDefaultValue(key: string, value: string): string | null {
  const signal = normalizeKey(key);
  const hasPlaceholder = /CHANGE_ME[A-Z0-9_]*/i.test(value);
  const isEmpty = value.length === 0;
  const shouldFillGeneric = hasPlaceholder || isEmpty;

  if (signal === "APP_ENV" || signal === "RAILS_ENV") {
    return "production";
  }

  if (signal === "NODE_ENV") {
    return "production";
  }

  if (signal.includes("LOG_LEVEL")) {
    return "info";
  }

  if (signal.includes("TELEMETRY_ENABLED") || signal.includes("PROMETHEUS_METRICS") || signal.includes("METRICS_ENABLED")) {
    return "false";
  }

  if (signal.includes("CORS_CREDENTIALS") || signal.includes("USE_SSL") || signal.includes("REQUIRE_SSL")) {
    return "true";
  }

  if (signal.includes("BOOTSTRAP_ADMIN_EMAIL") || signal.includes("ADMIN_EMAIL")) {
    return "admin@example.local";
  }

  if (signal.includes("SUPPORT_EMAIL")) {
    return "support@example.local";
  }

  if (signal.includes("CONTACT_EMAIL")) {
    return "contact@example.local";
  }

  if (shouldFillGeneric && signal.endsWith("EMAIL")) {
    return "admin@example.local";
  }

  if (shouldFillGeneric && (signal.includes("BASE_URL") || signal.endsWith("_URL") || signal.endsWith("_URI") || signal.includes("ORIGIN") || signal.includes("SITE_URL"))) {
    return "https://example.local";
  }

  if (shouldFillGeneric && signal.includes("CORS_ORIGIN")) {
    return "https://example.local";
  }

  if (shouldFillGeneric && signal.includes("SMTP_HOST")) {
    return "smtp.example.local";
  }

  if (shouldFillGeneric && signal.includes("SMTP_PORT")) {
    return "587";
  }

  if (shouldFillGeneric && signal.includes("SMTP_USER")) {
    return "smtp-user";
  }

  if (shouldFillGeneric && (signal.includes("SMTP_FROM") || signal.includes("MAIL_FROM") || signal.includes("SENDER_EMAIL"))) {
    return "noreply@example.local";
  }

  if (shouldFillGeneric && (signal.includes("S3_BUCKET") || signal.includes("BUCKET_NAME"))) {
    return "app-bucket";
  }

  if (shouldFillGeneric && (signal.includes("S3_REGION") || signal.includes("AWS_REGION") || signal.includes("SQS_REGION"))) {
    return "eu-central-1";
  }

  if (shouldFillGeneric && signal.includes("S3_ENDPOINT")) {
    return "https://s3.example.local";
  }

  if (shouldFillGeneric && signal.includes("RABBITMQ_URI")) {
    return "amqp://rabbitmq:5672";
  }

  if (shouldFillGeneric && signal.includes("REDIS_URL")) {
    return "redis://redis:6379/0";
  }

  if (shouldFillGeneric && signal.includes("PORT")) {
    if (signal.includes("POSTGRES")) {
      return "5432";
    }

    if (signal.includes("REDIS")) {
      return "6379";
    }

    if (signal.includes("SMTP")) {
      return "587";
    }

    if (signal.includes("S3") || signal.includes("MINIO")) {
      return "9000";
    }

    return "3000";
  }

  return null;
}

function normalizeKey(key: string): string {
  return key.trim().toUpperCase();
}

function addUnique(values: string[], value: string): string[] {
  const normalized = normalizeKey(value);
  return values.some((entry) => normalizeKey(entry) === normalized) ? values : [...values, value];
}

function openFileWithBrowserPicker(): Promise<{ name: string; content: string } | null> {
  return new Promise((resolve) => {
    const input = document.createElement("input");
    input.type = "file";
    input.accept = ".env,.txt,text/plain";

    input.addEventListener(
      "change",
      async () => {
        const file = input.files?.[0];
        if (!file) {
          resolve(null);
          return;
        }

        resolve({
          name: file.name,
          content: await file.text()
        });
      },
      { once: true }
    );

    input.click();
  });
}

function saveFileWithBrowserDownload(content: string) {
  const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const anchor = document.createElement("a");
  anchor.href = url;
  anchor.download = ".env";
  anchor.style.display = "none";

  document.body.append(anchor);
  anchor.click();
  anchor.remove();
  URL.revokeObjectURL(url);
}

export default function App() {
  const [input, setInput] = useState("");
  const [loadedPath, setLoadedPath] = useState<string | null>(null);
  const [copyLabel, setCopyLabel] = useState<"copy" | "copied">("copy");
  const [generation, setGeneration] = useState(0);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [themeMode, setThemeMode] = useState<ThemeMode>(readTheme);
  const [language, setLanguage] = useState<Language>(detectLanguage);
  const [manualDefaults, setManualDefaults] = useState<EnvDefault[]>(readDefaults);
  const [ignoredAutoDefaults, setIgnoredAutoDefaults] = useState<string[]>(readIgnoredAutoDefaults);
  const t = translations[language];
  const automaticDefaults = useMemo(() => inferAutomaticDefaults(input, manualDefaults, ignoredAutoDefaults), [input, manualDefaults, ignoredAutoDefaults]);
  const defaults = useMemo<DefaultRow[]>(
    () => [
      ...automaticDefaults.map((entry) => ({ ...entry, source: "auto" as const })),
      ...manualDefaults.map((entry, index) => ({ ...entry, index, source: "manual" as const }))
    ],
    [automaticDefaults, manualDefaults]
  );
  const result = useMemo(() => transformEnv(input, defaults), [input, defaults, generation]);

  useEffect(() => {
    document.documentElement.dataset.theme = themeMode;
    localStorage.setItem("envhelper-theme", themeMode);
  }, [themeMode]);

  useEffect(() => {
    localStorage.setItem("envhelper-language", language);
  }, [language]);

  useEffect(() => {
    localStorage.setItem("envhelper-defaults", JSON.stringify(manualDefaults.filter((entry) => canPersistDefault(entry.key))));
  }, [manualDefaults]);

  useEffect(() => {
    localStorage.setItem("envhelper-ignored-auto-defaults", JSON.stringify(ignoredAutoDefaults));
  }, [ignoredAutoDefaults]);

  async function openFile() {
    try {
      if (window.envHelper?.openFile) {
        const file = await window.envHelper.openFile();
        if (file) {
          setInput(file.content);
          setLoadedPath(file.path);
        }

        return;
      }
    } catch (error) {
      console.warn("Native file dialog failed, falling back to browser file input.", error);
    }

    const file = await openFileWithBrowserPicker();
    if (file) {
      setInput(file.content);
      setLoadedPath(file.name);
    }
  }

  async function saveFile() {
    try {
      if (window.envHelper?.saveFile) {
        await window.envHelper.saveFile(result.output);
        return;
      }
    } catch (error) {
      console.warn("Native save dialog failed, falling back to browser download.", error);
    }

    saveFileWithBrowserDownload(result.output);
  }

  async function copyOutput() {
    await navigator.clipboard.writeText(result.output);
    setCopyLabel("copied");
    window.setTimeout(() => setCopyLabel("copy"), 1400);
  }

  function updateDefault(row: DefaultRow, field: keyof EnvDefault, value: string) {
    if (row.source === "auto") {
      setIgnoredAutoDefaults((current) => addUnique(current, row.key));
      setManualDefaults((current) => [...current, { key: field === "key" ? value : row.key, value: field === "value" ? value : row.value }]);
      return;
    }

    if (row.index === undefined) {
      return;
    }

    setManualDefaults((current) => current.map((entry, entryIndex) => (entryIndex === row.index ? { ...entry, [field]: value } : entry)));
  }

  function addDefault() {
    setManualDefaults((current) => [...current, { key: "", value: "" }]);
  }

  function removeDefault(row: DefaultRow) {
    if (row.source === "auto") {
      setIgnoredAutoDefaults((current) => addUnique(current, row.key));
      return;
    }

    if (row.index === undefined) {
      return;
    }

    setManualDefaults((current) => current.filter((_, entryIndex) => entryIndex !== row.index));
  }

  return (
    <main className="appShell">
      <header className="toolbar">
        <div className="brand">
          <div className="brandMark">EH</div>
          <div>
            <h1>EnvHelper</h1>
            <p>{t.appSubtitle}</p>
          </div>
        </div>

        <div className="toolbarActions">
          <button className="secondary" onClick={openFile} type="button">
            <FileInput size={17} /> {t.loadFile}
          </button>
          <button className="secondary" onClick={saveFile} type="button" disabled={!result.output}>
            <FileDown size={17} /> {t.saveFile}
          </button>
          <div className="segmented" aria-label={t.theme}>
            {themeLabels.map((mode) => (
              <button
                className={themeMode === mode ? "active" : ""}
                key={mode}
                onClick={() => setThemeMode(mode)}
                type="button"
                title={t[mode]}
              >
                {mode === "system" ? <SunMoon size={15} /> : t[mode]}
              </button>
            ))}
          </div>
          <button className="iconButton" onClick={() => setSettingsOpen(true)} title={t.settings} type="button">
            <Settings size={18} />
          </button>
        </div>
      </header>

      <section className="statusStrip">
        <Stat label={t.hits} value={String(result.changedCount)} />
        <Stat label={t.values} value={String(result.replacements.length)} />
        <Stat label={t.defaultsApplied} value={String(result.defaultedKeys.length)} />
        <Stat label={t.source} value={loadedPath ? t.file : t.text} />
      </section>

      <section className="workbench">
        <EditorPanel
          hint={loadedPath ?? t.inputHint}
          title={t.input}
        >
          <textarea spellCheck={false} value={input} onChange={(event) => setInput(event.target.value)} aria-label="Env input" />
        </EditorPanel>

        <EditorPanel
          actions={
            <>
              <button className="iconButton" onClick={() => setGeneration((current) => current + 1)} title={t.regenerate} type="button">
                <RefreshCcw size={17} />
              </button>
              <button className="primary" onClick={copyOutput} type="button">
                {copyLabel === "copied" ? <Check size={17} /> : <Clipboard size={17} />} {copyLabel === "copied" ? t.copied : t.copy}
              </button>
            </>
          }
          hint={t.outputHint}
          title={t.output}
        >
          <textarea spellCheck={false} value={result.output} readOnly aria-label="Env output" />
        </EditorPanel>
      </section>

      <section className="lowerGrid">
        <section className="dataPanel">
          <PanelHeading icon={<Plus size={18} />} title={t.defaults} hint={t.defaultsHint} />
          <div className="defaultsList">
            {defaults.map((entry, index) => (
              <div className="defaultRow" key={`${entry.source}-${entry.key}-${index}`}>
                <input
                  aria-label={t.key}
                  placeholder={t.key}
                  value={entry.key}
                  onChange={(event) => updateDefault(entry, "key", event.target.value)}
                />
                <input
                  aria-label={t.value}
                  placeholder={t.value}
                  value={entry.value}
                  onChange={(event) => updateDefault(entry, "value", event.target.value)}
                />
                <span className={`defaultSource ${entry.source}`}>{entry.source === "auto" ? t.autoDefault : t.manualDefault}</span>
                <button className="iconButton subtle" onClick={() => removeDefault(entry)} title="Remove" type="button">
                  <Trash2 size={16} />
                </button>
              </div>
            ))}
          </div>
          <button className="secondary fullWidth" onClick={addDefault} type="button">
            <Plus size={17} /> {t.addDefault}
          </button>
        </section>

        <section className="dataPanel">
          <PanelHeading icon={<ShieldCheck size={18} />} title={t.detected} hint={t.detectedHint} />
          {result.replacements.length === 0 ? (
            <div className="empty">{t.noPlaceholders}</div>
          ) : (
            <div className="requirementsTable">
              <div className="tableHeader">
                <span>{t.placeholder}</span>
                <span>{t.keys}</span>
                <span>{t.format}</span>
              </div>
              {result.replacements.map((replacement) => (
                <div className="tableRow" key={replacement.placeholder}>
                  <code>{replacement.placeholder}</code>
                  <span>{replacement.keys.join(", ")}</span>
                  <strong>{replacement.format}</strong>
                </div>
              ))}
            </div>
          )}
        </section>
      </section>

      {settingsOpen ? (
        <aside className="settingsOverlay" aria-label={t.settings}>
          <div className="settingsPanel">
            <div className="settingsHeader">
              <div>
                <h2>{t.settings}</h2>
                <p>{t.appSubtitle}</p>
              </div>
              <button className="iconButton" onClick={() => setSettingsOpen(false)} title="Close" type="button">
                <X size={18} />
              </button>
            </div>

            <div className="settingsGroup">
              <label>
                <Languages size={18} /> {t.language}
              </label>
              <div className="languageGrid">
                {(Object.keys(languageNames) as Language[]).map((code) => (
                  <button className={language === code ? "selected" : ""} key={code} onClick={() => setLanguage(code)} type="button">
                    {languageNames[code]}
                  </button>
                ))}
              </div>
            </div>

            <div className="settingsMeta">
              <span>{t.version}: {packageInfo.version}</span>
              <span>{t.author}: {packageInfo.author}</span>
              <span>{t.repository}: MrSphay/envHelper</span>
            </div>
          </div>
        </aside>
      ) : null}
    </main>
  );
}

function Stat({ label, value }: { label: string; value: string }) {
  return (
    <div className="statItem">
      <span>{value}</span>
      <p>{label}</p>
    </div>
  );
}

function EditorPanel({ actions, children, hint, title }: { actions?: ReactNode; children: ReactNode; hint: string; title: string }) {
  return (
    <section className="editorPanel">
      <div className="panelHeader">
        <div>
          <h2>{title}</h2>
          <p>{hint}</p>
        </div>
        {actions ? <div className="buttonRow">{actions}</div> : null}
      </div>
      {children}
    </section>
  );
}

function PanelHeading({ hint, icon, title }: { hint: string; icon: ReactNode; title: string }) {
  return (
    <div className="panelHeading">
      <div>
        <h2>{title}</h2>
        <p>{hint}</p>
      </div>
      {icon}
    </div>
  );
}
