"use client";

import { useCallback, useState } from "react";
import { Icon } from "@/components/ui/Icon";

// ──────────────────────────────────────────────────────────────────────────
// Einstellbarer Passwortgenerator. Nutzt `crypto.getRandomValues` mit
// Rejection Sampling — kein `Math.random()`, keine Verzerrung der Verteilung.
// ──────────────────────────────────────────────────────────────────────────

const SETS = {
  lower: "abcdefghijklmnopqrstuvwxyz",
  upper: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
  digits: "0123456789",
  symbols: "!@#$%^&*()-_=+[]{}:;,.?/~",
} as const;

// Zeichen, die in vielen Schriftarten verwechselbar sind.
const AMBIGUOUS = new Set("O0oIl1|`'\"{}[]()/\\;:.,<>~".split(""));

export type GeneratorOptions = {
  length: number;
  lower: boolean;
  upper: boolean;
  digits: boolean;
  symbols: boolean;
  avoidAmbiguous: boolean;
};

export const DEFAULT_OPTIONS: GeneratorOptions = {
  length: 20,
  lower: true,
  upper: true,
  digits: true,
  symbols: true,
  avoidAmbiguous: true,
};

const STORAGE_KEY = "avaria:vault:generator";
const LENGTH_PRESETS = [12, 16, 24, 32];

function poolFor(o: GeneratorOptions): string[] {
  let pool = "";
  if (o.lower) pool += SETS.lower;
  if (o.upper) pool += SETS.upper;
  if (o.digits) pool += SETS.digits;
  if (o.symbols) pool += SETS.symbols;
  const chars = [...new Set(pool.split(""))];
  return o.avoidAmbiguous ? chars.filter((c) => !AMBIGUOUS.has(c)) : chars;
}

/** Gleichverteilte Zufallsauswahl ohne Modulo-Bias. */
function pick(chars: string[]): string {
  const max = Math.floor(256 / chars.length) * chars.length;
  const buf = new Uint8Array(1);
  for (;;) {
    crypto.getRandomValues(buf);
    if (buf[0] < max) return chars[buf[0] % chars.length];
  }
}

export function generatePassword(o: GeneratorOptions): string {
  const chars = poolFor(o);
  if (chars.length === 0) return "";

  // Je aktivierter Zeichengruppe mindestens ein Zeichen garantieren.
  const groups: string[][] = [];
  for (const [key, set] of Object.entries(SETS) as [keyof typeof SETS, string][]) {
    if (!o[key]) continue;
    const g = chars.filter((c) => set.includes(c));
    if (g.length > 0) groups.push(g);
  }

  const out: string[] = [];
  for (const g of groups.slice(0, o.length)) out.push(pick(g));
  while (out.length < o.length) out.push(pick(chars));

  // Fisher-Yates, damit die garantierten Zeichen nicht immer vorne stehen.
  for (let i = out.length - 1; i > 0; i--) {
    const r = new Uint32Array(1);
    crypto.getRandomValues(r);
    const j = r[0] % (i + 1);
    [out[i], out[j]] = [out[j], out[i]];
  }
  return out.join("");
}

/** Entropie in Bit — die ehrliche Stärkeangabe (Pool^Länge). */
export function entropyBits(o: GeneratorOptions): number {
  const size = poolFor(o).length;
  return size > 1 ? Math.round(o.length * Math.log2(size)) : 0;
}

export function strengthOf(bits: number): { label: string; color: string; pct: number } {
  if (bits < 50) return { label: "schwach", color: "var(--pink)", pct: 25 };
  if (bits < 75) return { label: "solide", color: "#ffc36b", pct: 55 };
  if (bits < 100) return { label: "stark", color: "#7ee787", pct: 80 };
  return { label: "sehr stark", color: "#7ee787", pct: 100 };
}

/** Zuletzt genutzte Einstellungen des Nutzers (oder Standardwerte). */
function loadOptions(): GeneratorOptions {
  try {
    const raw = localStorage.getItem(STORAGE_KEY);
    if (raw) return { ...DEFAULT_OPTIONS, ...JSON.parse(raw) };
  } catch {
    /* Standardwerte behalten */
  }
  return DEFAULT_OPTIONS;
}

const SET_META: { key: keyof GeneratorOptions; sample: string; label: string }[] = [
  { key: "upper", sample: "AB", label: "Groß" },
  { key: "lower", sample: "ab", label: "Klein" },
  { key: "digits", sample: "12", label: "Zahlen" },
  { key: "symbols", sample: "#!", label: "Zeichen" },
];

export function PasswordGenerator({
  onUse,
  onCopy,
}: {
  onUse: (value: string) => void;
  onCopy?: (value: string) => void;
}) {
  // Der Generator erscheint erst nach einer Nutzeraktion, läuft also nie im
  // Server-Rendering — Einstellungen dürfen direkt initial geladen werden.
  const [initial] = useState(loadOptions);
  const [opts, setOpts] = useState<GeneratorOptions>(initial);
  const [value, setValue] = useState(() => generatePassword(initial));

  const update = (patch: Partial<GeneratorOptions>) => {
    const next = { ...opts, ...patch };
    // Mindestens eine Zeichengruppe muss aktiv bleiben.
    if (!next.lower && !next.upper && !next.digits && !next.symbols) return;
    try {
      localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
    } catch {
      /* ignorieren */
    }
    setOpts(next);
    setValue(generatePassword(next));
  };

  const roll = useCallback(() => setValue(generatePassword(opts)), [opts]);

  const bits = entropyBits(opts);
  const strength = strengthOf(bits);

  return (
    <div className="gen-panel">
      {/* Ausgabe */}
      <div className="gen-output">
        <code className="gen-value">{value || "—"}</code>
        <button
          type="button"
          className="vault-icon-btn"
          style={{ opacity: 1, width: 28, height: 28 }}
          onClick={roll}
          title="Neu erzeugen"
        >
          <Icon name="activity" size={16} />
        </button>
        {onCopy ? (
          <button
            type="button"
            className="vault-icon-btn"
            style={{ opacity: 1, width: 28, height: 28 }}
            onClick={() => onCopy(value)}
            title="Kopieren"
          >
            <Icon name="copy" size={16} />
          </button>
        ) : null}
      </div>

      {/* Stärke */}
      <div className="gen-row">
        <div className="gen-meter flex-1">
          <i style={{ width: `${strength.pct}%`, background: strength.color }} />
        </div>
        <span className="text-xs font-black" style={{ color: strength.color }}>
          {bits} Bit · {strength.label}
        </span>
      </div>

      {/* Länge */}
      <div className="flex flex-col gap-2">
        <div className="gen-row">
          <span className="gen-label">Länge</span>
          <input
            type="range"
            className="range flex-1"
            min={8}
            max={64}
            value={opts.length}
            onChange={(e) => update({ length: Number(e.target.value) })}
            aria-label="Länge"
          />
          <span className="gen-len">{opts.length}</span>
        </div>
        <div className="flex flex-wrap gap-1.5">
          {LENGTH_PRESETS.map((n) => (
            <button
              key={n}
              type="button"
              className="gen-chip"
              data-active={opts.length === n}
              onClick={() => update({ length: n })}
            >
              {n}
            </button>
          ))}
        </div>
      </div>

      {/* Zeichengruppen */}
      <div className="gen-sets">
        {SET_META.map((s) => (
          <button
            key={s.key}
            type="button"
            className="gen-set"
            data-active={Boolean(opts[s.key])}
            onClick={() => update({ [s.key]: !opts[s.key] } as Partial<GeneratorOptions>)}
            title={`${s.label} ${opts[s.key] ? "aus" : "ein"}schalten`}
          >
            <span className="gen-set-sample">{s.sample}</span>
            <span className="gen-set-label">{s.label}</span>
          </button>
        ))}
      </div>

      {/* Sonderoption */}
      <button
        type="button"
        className="gen-switch"
        data-active={opts.avoidAmbiguous}
        onClick={() => update({ avoidAmbiguous: !opts.avoidAmbiguous })}
      >
        <span className="gen-switch-track">
          <span className="gen-switch-knob" />
        </span>
        <span className="flex-1">Verwechselbare Zeichen meiden</span>
        <span className="vault-mono text-xs" style={{ color: "var(--muted)" }}>
          0 O l 1
        </span>
      </button>

      <button type="button" className="btn btn-primary btn-sm" onClick={() => onUse(value)}>
        <Icon name="check" size={15} /> Passwort übernehmen
      </button>
    </div>
  );
}
