"use client";

import * as React from "react";
import { CheckIcon, PlugZapIcon } from "lucide-react";

import { Example } from "@/components/design-system/example";
import { Section } from "@/components/design-system/section";
import { SecretInput, cleanSecret, type SecretStatus } from "@/components/medora/secret-input";
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/spinner";
import { toast } from "@/components/ui/toast";

type Key = "passkey" | "consumer" | "callback" | "etims";

const LABEL: Record<string, string> = {
  passkey: "Passkey",
  consumerKey: "Consumer key",
  consumerSecret: "Consumer secret",
  callbackSecret: "Callback secret",
  etimsKey: "eTIMS API key",
};

const today = () => new Date().toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" });

export function SecretInputSection() {
  const [status, setStatus] = React.useState<Record<Key, SecretStatus>>({
    passkey: { set: true, lastChanged: { at: "12 Oct 2026", by: "Wanjiru Kamau" } },
    consumer: { set: true, lastChanged: { at: "12 Oct 2026", by: "Wanjiru Kamau" } },
    callback: { set: false },
    etims: { set: true, lastChanged: { at: "2 Sep 2026", by: "Brian Otieno" } },
  });
  const [pending, setPending] = React.useState<Partial<Record<Key, Record<string, string> | null>>>({});
  const [errors, setErrors] = React.useState<Record<string, string | undefined>>({});
  const [saving, setSaving] = React.useState(false);
  const [lastBody, setLastBody] = React.useState<string | null>(null);
  const [testing, setTesting] = React.useState<"idle" | "busy" | "ok">("idle");
  const [formKey, setFormKey] = React.useState(0);

  const track = (k: Key) => (v: Record<string, string> | null) => {
    setPending((p) => ({ ...p, [k]: v }));
    setErrors({});
  };

  const save = async () => {
    // Only fields being replaced are validated and sent; an untouched secret is not in the request at all.
    const body: Record<string, string> = {};
    const errs: Record<string, string> = {};
    const notes: string[] = [];
    for (const v of Object.values(pending)) {
      if (!v) continue;
      for (const [name, raw] of Object.entries(v)) {
        const { value, note } = cleanSecret(raw);
        if (!value) errs[name] = `Enter the ${LABEL[name].toLowerCase()} or choose Cancel`;
        else body[name] = value;
        if (note && value) notes.push(`${LABEL[name]}: ${note}`);
      }
    }
    if (Object.keys(errs).length) return setErrors(errs);
    if (!Object.keys(body).length) return toast.add({ title: "Nothing to save", description: "No secret is being replaced." });

    setSaving(true);
    // The preview shows names and lengths only; the values never leave this function in the demo.
    setLastBody(JSON.stringify(Object.fromEntries(Object.entries(body).map(([k, v]) => [k, `<${v.length} characters>`])), null, 2));
    await new Promise((r) => setTimeout(r, 900));
    setSaving(false);
    const stamp = { at: today(), by: "you" };
    setStatus((s) => ({
      ...s,
      ...(pending.passkey ? { passkey: { set: true, lastChanged: stamp } } : {}),
      ...(pending.consumer ? { consumer: { set: true, lastChanged: stamp } } : {}),
      ...(pending.callback ? { callback: { set: true, lastChanged: stamp } } : {}),
      ...(pending.etims ? { etims: { set: true, lastChanged: stamp } } : {}),
    }));
    setPending({});
    setFormKey((k) => k + 1);
    toast.add({ type: "success", title: "M-Pesa settings saved", description: notes.length ? notes.join(" ") : "The new values won't be shown again." });
  };

  const replacingAny = Object.values(pending).some(Boolean);

  return (
    <Section
      id="secret-input"
      title="Secret input"
      description="Integration keys (M-Pesa passkey and consumer key and secret, eTIMS, SMS). A saved secret is never returned to the browser, so the field says Set or Not set in words and offers Replace; there is no Show for a saved value. While replacing, the new value is masked with a Show toggle for checking what was typed. Untouched secrets are left out of the save request. Identifiers that are not secret, like the shortcode, are ordinary fields."
    >
      <div className="grid gap-4 lg:grid-cols-[minmax(0,1fr)_18rem]">
        <Example title="M-Pesa (Daraja) settings" description="Replace a key, try Save empty, paste a value with a trailing space, Escape to cancel">
          <div key={formKey} className="space-y-5">
            <div className="grid gap-3 sm:grid-cols-2">
              <Field>
                <FieldLabel htmlFor="mp-shortcode">Business shortcode</FieldLabel>
                <Input id="mp-shortcode" defaultValue="174379" className="tabular-nums" />
              </Field>
              <Field>
                <FieldLabel htmlFor="mp-till">Till number</FieldLabel>
                <Input id="mp-till" defaultValue="5123456" className="tabular-nums" />
              </Field>
            </div>
            <div className="divide-y divide-border rounded-lg border border-border [&>*]:p-3">
              <SecretInput
                fields={[{ name: "passkey", label: "Passkey" }]}
                status={status.passkey}
                hint="Paste the passkey from the Safaricom developer portal. It won't be shown again after you save."
                errors={errors}
                onReplace={track("passkey")}
              />
              <SecretInput
                fields={[
                  { name: "consumerKey", label: "Consumer key" },
                  { name: "consumerSecret", label: "Consumer secret" },
                ]}
                status={status.consumer}
                hint="Replace both together: Safaricom issues them as a pair."
                errors={errors}
                onReplace={track("consumer")}
              />
              <SecretInput
                fields={[{ name: "callbackSecret", label: "Callback secret" }]}
                status={status.callback}
                required
                requiredHint="M-Pesa payment confirmations won't be accepted until this is added."
                errors={errors}
                onReplace={track("callback")}
              />
              <SecretInput
                fields={[{ name: "etimsKey", label: "eTIMS API key" }]}
                status={status.etims}
                removable
                errors={errors}
                onReplace={track("etims")}
                onRemove={async () => {
                  await new Promise((r) => setTimeout(r, 700));
                  setStatus((s) => ({ ...s, etims: { set: false } }));
                  toast.add({ title: "eTIMS API key removed" });
                }}
              />
            </div>
            <div className="flex flex-wrap items-center justify-between gap-3 border-t border-border pt-3">
              <Button
                variant="outline"
                disabled={testing === "busy"}
                onClick={async () => {
                  setTesting("busy");
                  await new Promise((r) => setTimeout(r, 1200));
                  setTesting("ok");
                }}
              >
                {testing === "busy" ? <Spinner /> : testing === "ok" ? <CheckIcon data-icon="inline-start" /> : <PlugZapIcon data-icon="inline-start" />}
                {testing === "busy" ? "Testing" : testing === "ok" ? "Connected to Safaricom (sandbox)" : "Test connection"}
              </Button>
              <Button onClick={save} disabled={saving || !replacingAny}>
                {saving ? <Spinner /> : null}
                {saving ? "Saving" : "Save"}
              </Button>
            </div>
          </div>
        </Example>
        <div className="space-y-4">
          <Example title="Last save request" description="Names and lengths only">
            <pre className="min-h-24 overflow-x-auto rounded-md bg-muted p-3 font-mono text-xs leading-5 text-foreground">{lastBody ?? "Nothing sent yet."}</pre>
          </Example>
          <Example title="Read only" description="A role that may view integrations but not change them">
            <SecretInput fields={[{ name: "ro", label: "Passkey" }]} status={status.passkey} readOnly onReplace={() => {}} />
          </Example>
        </div>
      </div>
    </Section>
  );
}
