<script lang="ts">
  import { onMount, onDestroy } from "svelte";
  import { env as publicEnv } from "$env/dynamic/public";
  import Button from "$lib/ui/Button.svelte";
  import TextInput from "$lib/ui/TextInput.svelte";
  import Textarea from "$lib/ui/Textarea.svelte";
  import Checkbox from "$lib/ui/Checkbox.svelte";
  import { useTranslate, T } from "$lib/translations";
  import {
    FILE_ACCEPT,
    LIMITS,
    validateFiles,
    validateMitmachen,
    type MitmachenErrors,
    type MitmachenFields,
  } from "./mitmachen-form-validation";

  type FormState = "idle" | "pending" | "success" | "error";

  const t = useTranslate();

  const TURNSTILE_SITE_KEY = publicEnv.PUBLIC_TURNSTILE_SITE_KEY ?? "";
  const TURNSTILE_ENABLED = TURNSTILE_SITE_KEY !== "";

  let title = $state("");
  let description = $state("");
  let contactName = $state("");
  let phone = $state("");
  let email = $state("");
  let consent = $state(false);
  let files = $state<File[]>([]);
  let fileInputEl = $state<HTMLInputElement | null>(null);

  // Honeypots — three with realistic names. Real users never see them.
  let honeypot = $state("");
  let website = $state("");
  let company = $state("");

  const pageLoadedAt = Date.now();

  let formState = $state<FormState>("idle");
  let serverError = $state("");
  let submitAttempted = $state(false);

  let turnstileToken = $state("");
  let turnstileEl = $state<HTMLDivElement | null>(null);
  let turnstileWidgetId: string | null = null;

  const fields = $derived<MitmachenFields>({
    title,
    description,
    contactName,
    phone,
    email,
    consent,
  });
  const errors = $derived<MitmachenErrors>(validateMitmachen(fields));
  const fileError = $derived(validateFiles(files));
  const isValid = $derived(Object.keys(errors).length === 0 && !fileError);

  function showError(field: keyof MitmachenFields): string {
    if (!submitAttempted) return "";
    const e = errors[field];
    return e ? t(e.key, e.params) : "";
  }

  const descriptionRemaining = $derived(LIMITS.DESCRIPTION_MAX - description.length);
  const descriptionHelp = $derived(
    descriptionRemaining >= 0
      ? t(T.contact_message_remaining, { n: descriptionRemaining })
      : t(T.contact_message_overflow, { n: -descriptionRemaining }),
  );

  function onFilesChosen(e: Event) {
    const input = e.currentTarget as HTMLInputElement;
    const chosen = [...(input.files ?? [])];
    // Anhängen statt ersetzen — Nutzer kann in mehreren Schritten auswählen.
    files = [...files, ...chosen].slice(0, LIMITS.MAX_FILES + 1);
    input.value = "";
  }

  function removeFile(idx: number) {
    files = files.filter((_, i) => i !== idx);
  }

  function fmtSize(bytes: number): string {
    if (bytes >= 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
    return `${Math.max(1, Math.round(bytes / 1024))} KB`;
  }

  type TurnstileAPI = {
    render: (
      el: HTMLElement,
      opts: {
        sitekey: string;
        theme?: "light" | "dark" | "auto";
        size?: "normal" | "compact" | "flexible";
        callback?: (token: string) => void;
        "expired-callback"?: () => void;
        "error-callback"?: () => void;
      },
    ) => string;
    reset: (id?: string) => void;
    remove: (id: string) => void;
  };

  function getTurnstile(): TurnstileAPI | undefined {
    return (window as unknown as { turnstile?: TurnstileAPI }).turnstile;
  }

  function renderTurnstile() {
    if (!TURNSTILE_ENABLED || !turnstileEl) return;
    const ts = getTurnstile();
    if (!ts) return;
    if (turnstileWidgetId !== null) return;
    turnstileWidgetId = ts.render(turnstileEl, {
      sitekey: TURNSTILE_SITE_KEY,
      theme: "light",
      size: "flexible",
      callback: (token) => {
        turnstileToken = token;
      },
      "expired-callback": () => {
        turnstileToken = "";
      },
      "error-callback": () => {
        turnstileToken = "";
      },
    });
  }

  function loadTurnstileScript() {
    if (!TURNSTILE_ENABLED) return;
    if (document.querySelector("script[data-turnstile]")) {
      renderTurnstile();
      return;
    }
    const s = document.createElement("script");
    s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
    s.async = true;
    s.defer = true;
    s.dataset.turnstile = "1";
    s.onload = renderTurnstile;
    document.head.appendChild(s);
  }

  onMount(loadTurnstileScript);
  onDestroy(() => {
    if (turnstileWidgetId !== null) {
      getTurnstile()?.remove(turnstileWidgetId);
      turnstileWidgetId = null;
    }
  });

  function resetTurnstile() {
    if (!TURNSTILE_ENABLED) return;
    turnstileToken = "";
    if (turnstileWidgetId !== null) getTurnstile()?.reset(turnstileWidgetId);
  }

  async function handleSubmit(e: SubmitEvent) {
    e.preventDefault();
    submitAttempted = true;
    if (formState === "pending") return;
    if (!isValid) {
      const first = Object.keys(errors)[0];
      const el = document.querySelector<HTMLElement>(`[data-field="${first}"]`);
      el?.focus();
      return;
    }
    if (TURNSTILE_ENABLED && !turnstileToken) {
      serverError = t(T.contact_captcha_pending);
      formState = "error";
      return;
    }

    formState = "pending";
    serverError = "";

    try {
      const fd = new FormData();
      fd.set("title", title);
      fd.set("description", description);
      fd.set("contactName", contactName);
      fd.set("phone", phone);
      fd.set("email", email);
      fd.set("consent", consent ? "true" : "false");
      fd.set("_honeypot", honeypot);
      fd.set("website", website);
      fd.set("company", company);
      fd.set("_client_age_ms", String(Date.now() - pageLoadedAt));
      fd.set("cf_turnstile_token", turnstileToken);
      for (const f of files) fd.append("file", f, f.name);

      const res = await fetch("/api/mitmachen", { method: "POST", body: fd });

      if (res.ok) {
        formState = "success";
        return;
      }

      const body = await res.json().catch(() => ({}));
      if (res.status === 422) {
        serverError = t(T.contact_server_error_validation);
      } else if (res.status === 413) {
        serverError = t(T.mitmachen_error_files_size, { mb: LIMITS.MAX_FILE_MB });
      } else if (res.status === 429) {
        serverError = t(T.contact_server_error_throttle);
      } else if (res.status === 403) {
        serverError =
          body?.error === "captcha"
            ? t(T.contact_server_error_captcha)
            : t(T.contact_server_error_blocked);
      } else {
        serverError = body?.error || t(T.contact_server_error_generic);
      }
      formState = "error";
      resetTurnstile();
    } catch {
      serverError = t(T.contact_server_error_network);
      formState = "error";
      resetTurnstile();
    }
  }
</script>

{#if formState === "success"}
  <div class="rounded-sm border border-wald-300 bg-wald-50 px-4 py-5 text-center shadow-sm">
    <p class="text-base font-semibold text-wald-700">{t(T.mitmachen_success_title)}</p>
    <p class="mt-1 text-sm text-stein-600">{t(T.mitmachen_success_body)}</p>
  </div>
{:else}
  <form
    onsubmit={handleSubmit}
    novalidate
    class="space-y-3"
    aria-busy={formState === "pending"}
  >
    <!-- Honeypots: visually hidden, off the keyboard, off the a11y tree -->
    <div aria-hidden="true" class="pointer-events-none absolute left-[-9999px] top-auto h-px w-px overflow-hidden opacity-0">
      <label for="mitmachen-hp-1">leer lassen</label>
      <input id="mitmachen-hp-1" name="_honeypot" type="text" tabindex="-1" autocomplete="off" bind:value={honeypot} />
      <label for="mitmachen-hp-2">Website</label>
      <input id="mitmachen-hp-2" name="website" type="url" tabindex="-1" autocomplete="off" bind:value={website} />
      <label for="mitmachen-hp-3">Firma</label>
      <input id="mitmachen-hp-3" name="company" type="text" tabindex="-1" autocomplete="off" bind:value={company} />
    </div>

    <div data-field="title">
      <TextInput
        size="sm"
        name="title"
        label={t(T.mitmachen_label_title)}
        placeholder={t(T.mitmachen_placeholder_title)}
        required
        bind:value={title}
        error={showError("title")}
      />
    </div>

    <div data-field="description">
      <Textarea
        size="sm"
        name="description"
        label={t(T.mitmachen_label_description)}
        placeholder={t(T.mitmachen_placeholder_description)}
        required
        rows={5}
        bind:value={description}
        error={showError("description")}
        helpText={descriptionHelp}
      />
    </div>

    <div data-field="contactName">
      <TextInput
        size="sm"
        name="contactName"
        label={t(T.mitmachen_label_contact_name)}
        placeholder={t(T.mitmachen_placeholder_contact_name)}
        bind:value={contactName}
        error={showError("contactName")}
      />
    </div>

    <div class="grid gap-3 sm:grid-cols-2">
      <div data-field="phone">
        <TextInput
          size="sm"
          name="phone"
          type="tel"
          label={t(T.mitmachen_label_phone)}
          placeholder={t(T.mitmachen_placeholder_phone)}
          bind:value={phone}
          error={showError("phone")}
        />
      </div>
      <div data-field="email">
        <TextInput
          size="sm"
          name="email"
          type="email"
          label={t(T.mitmachen_label_email)}
          placeholder={t(T.mitmachen_placeholder_email)}
          bind:value={email}
          error={showError("email")}
        />
      </div>
    </div>

    <div data-field="files">
      <span class="mb-1 block text-sm font-medium text-stein-700">{t(T.mitmachen_label_files)}</span>
      <input
        bind:this={fileInputEl}
        type="file"
        multiple
        accept={FILE_ACCEPT}
        class="sr-only"
        onchange={onFilesChosen}
        tabindex="-1"
      />
      <!-- Nativer Button: ui/Button forwarded kein onclick. Klassen = Button
           secondary/sm. -->
      <button
        type="button"
        class="inline-flex h-9 items-center justify-center gap-2 rounded-xs border-[1.5px] border-wald-500 bg-transparent px-4 py-2 text-sm font-semibold text-wald-500 transition-colors duration-150 hover:bg-wald-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-wald-300 focus-visible:ring-offset-2 active:bg-wald-100"
        onclick={() => fileInputEl?.click()}
      >{t(T.mitmachen_files_add)}</button>
      <p class="mt-1 text-[0.7rem] text-stein-500">
        {t(T.mitmachen_files_help, { n: LIMITS.MAX_FILES, mb: LIMITS.MAX_FILE_MB })}
      </p>
      {#if files.length > 0}
        <ul class="mt-2 space-y-1">
          {#each files as f, idx}
            <li class="flex items-center gap-2 rounded-sm border border-stein-200 bg-stein-50 px-2 py-1 text-xs">
              <span class="min-w-0 flex-1 truncate">{f.name}</span>
              <span class="shrink-0 text-stein-500">{fmtSize(f.size)}</span>
              <button
                type="button"
                class="shrink-0 text-stein-500 underline hover:text-stein-700"
                onclick={() => removeFile(idx)}
              >{t(T.mitmachen_files_remove)}</button>
            </li>
          {/each}
        </ul>
      {/if}
      {#if submitAttempted && fileError}
        <p class="mt-1 text-[0.7rem] text-error" role="alert">{t(fileError.key, fileError.params)}</p>
      {/if}
    </div>

    <div data-field="consent" class="pt-1">
      <Checkbox
        size="sm"
        name="consent"
        bind:checked={consent}
        label={t(T.mitmachen_label_consent)}
      />
      {#if showError("consent")}
        <p class="mt-1 text-[0.7rem] text-error" role="alert">{showError("consent")}</p>
      {/if}
      <p class="mt-1 text-[0.7rem] leading-snug text-stein-500">{t(T.mitmachen_privacy_note)}</p>
    </div>

    {#if TURNSTILE_ENABLED}
      <div bind:this={turnstileEl} class="pt-1"></div>
    {/if}

    {#if serverError}
      <div class="rounded-sm border border-error bg-error-subtle px-3 py-2 text-sm text-error shadow-sm" role="alert">
        {serverError}
      </div>
    {/if}

    <div class="flex items-center gap-3 pt-2">
      <Button
        type="submit"
        variant="primary"
        size="sm"
        loading={formState === "pending"}
        disabled={!isValid && submitAttempted}
        label={formState === "pending" ? t(T.contact_submitting) : t(T.mitmachen_submit)}
      />
      {#if !isValid && submitAttempted && !serverError}
        <span class="text-xs text-error">{t(T.contact_fix_errors)}</span>
      {/if}
    </div>
  </form>
{/if}
