/**
 * @file WebhookSettings.tsx
 * @description Settings-page panel for universal webhook notifications across 14
 * first-class providers (Slack, Discord, Teams, Google Chat, Mattermost,
 * Rocket.Chat, Telegram, PagerDuty, Opsgenie, Splunk On-Call, Zapier, Make, n8n,
 * Pipedream) plus a generic endpoint. The form is driven by provider metadata
 * fetched from the server (`/api/webhooks/providers`): each provider declares
 * whether it needs a URL and which credential fields to render, so adding a
 * provider server-side surfaces here with no UI change. Secrets are never
 * returned by the API - URLs are masked and re-entered to change.
 * @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
 */
/* =============================================================================
 * MODULE_GUIDE — extended in-file reference (comments only; safe to read, never executed)
 * =============================================================================
 * **Purpose:** React hook: isolates side effects and subscription wiring so presentational components stay declarative.
 *
 * ## Design constraints
 * - Local-first: no telemetry leaves the machine unless the user configures webhooks.
 * - Fail-safe hooks path on the server must never block Claude Code; UI mirrors that
 *   philosophy by degrading gracefully (empty states, stale badges, reconnect loops).
 * - Destructive flows stay behind explicit confirmation modals and server-side gates.
 * - Internationalization: user-visible strings belong in i18n JSON, not literals here.
 *
 * ## Remote data & SSH
 * Remote Data Sources let operators aggregate multiple machines. SSH entries describe
 * how to reach a peer dashboard; the global data scope (`dataScope.ts`) narrows every
 * scoped GET via `?sources=`. Health checks and import history surface in Settings.
 *
 * ## Internal dependencies
 * - `../lib/api`
 * - `./Select`
 * - `./ConfirmModal`
 * - `./Checkbox`
 * - `./webhookGuides`
 * - `../lib/format`
 * - `../lib/types`
 *
 * ## Public surface
 * - `WebhookSettings` — exported API; see TSDoc on the symbol for behavior.
 *
 * ## Testing pointers
 * - Prefer colocated `__tests__` with Vitest + Testing Library for UI.
 * - Server contract changes require `npm run test:server` and OpenAPI sync.
 * - MCP edits: `npm run mcp:typecheck` and `npm run mcp:build`.
 *
 * ## Related docs
 * - `ARCHITECTURE.md` — hooks → API → SQLite → WebSocket → UI pipeline.
 * - `docs/API.md` — REST reference.
 * - `.claude/skills/file-headers/` — mandatory `@author` header policy.
 * ============================================================================= */
/* -----------------------------------------------------------------------------
 * EXPORT CATALOG — quick index of symbols defined below (documentation only).
 * -----------------------------------------------------------------------------
 * **WebhookSettings**
 *   Part of this module's public contract. Downstream imports should treat
 *   the signature and return type as stable unless release notes say otherwise.
 *   When behavior changes, update the `@file` overview and relevant tests.
 *
 * ----------------------------------------------------------------------------- */

import { useCallback, useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import {
  Webhook,
  Plus,
  Trash2,
  X,
  Pencil,
  Zap,
  Check,
  AlertTriangle,
  CheckCircle,
  XCircle,
  Loader2,
  BookOpen,
  ChevronDown,
  ExternalLink,
  Info,
} from "lucide-react";
import { api } from "../lib/api";
import { Select } from "./Select";
import { ConfirmModal } from "./ConfirmModal";
import { Checkbox } from "./Checkbox";
import { WEBHOOK_DOCS } from "./webhookGuides";
import { timeAgo } from "../lib/format";
import type {
  AlertRule,
  WebhookProvider,
  WebhookTarget,
  WebhookType,
  WebhookTestResult,
} from "../lib/types";

// Brand-ish accent per provider type; anything unmapped falls back to neutral.
const TYPE_STYLES: Partial<Record<WebhookType, string>> = {
  slack: "text-[#E01E5A] bg-[#E01E5A]/10 border-[#E01E5A]/20",
  discord: "text-[#5865F2] bg-[#5865F2]/10 border-[#5865F2]/20",
  teams: "text-[#6264A7] bg-[#6264A7]/10 border-[#6264A7]/20",
  google_chat: "text-[#1A73E8] bg-[#1A73E8]/10 border-[#1A73E8]/20",
  mattermost: "text-[#0058CC] bg-[#0058CC]/10 border-[#0058CC]/20",
  rocketchat: "text-[#F5455C] bg-[#F5455C]/10 border-[#F5455C]/20",
  telegram: "text-[#26A5E4] bg-[#26A5E4]/10 border-[#26A5E4]/20",
  pagerduty: "text-[#06AC38] bg-[#06AC38]/10 border-[#06AC38]/20",
  opsgenie: "text-[#2684FF] bg-[#2684FF]/10 border-[#2684FF]/20",
  splunk_oncall: "text-[#F99D1C] bg-[#F99D1C]/10 border-[#F99D1C]/20",
};
const NEUTRAL_STYLE = "text-fg-secondary bg-surface-2 border-border";

interface HeaderRow {
  key: string;
  value: string;
}

interface FormState {
  id: string | null;
  name: string;
  type: WebhookType;
  url: string;
  secret: string;
  headerRows: HeaderRow[];
  replaceHeaders: boolean;
  config: Record<string, string>;
  scopeAll: boolean;
  ruleIds: string[];
  enabled: boolean;
}

function defaultsFor(provider: WebhookProvider | undefined): Record<string, string> {
  const out: Record<string, string> = {};
  if (!provider) return out;
  for (const f of provider.fields) if (f.default != null) out[f.key] = f.default;
  return out;
}

function Toggle({
  checked,
  onChange,
  label,
}: {
  checked: boolean;
  onChange: (v: boolean) => void;
  label?: string;
}) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={checked}
      aria-label={label}
      onClick={() => onChange(!checked)}
      className={`relative inline-flex h-5 w-9 flex-shrink-0 items-center rounded-full transition-colors ${
        checked ? "bg-blue-600" : "bg-surface-4"
      }`}
    >
      <span
        className="inline-block h-3.5 w-3.5 rounded-full bg-white transition-transform"
        style={{ transform: checked ? "translateX(18px)" : "translateX(3px)" }}
      />
    </button>
  );
}

export function WebhookSettings() {
  const { t } = useTranslation("settings");
  const [targets, setTargets] = useState<WebhookTarget[]>([]);
  const [providers, setProviders] = useState<WebhookProvider[]>([]);
  const [rules, setRules] = useState<AlertRule[]>([]);
  const [loading, setLoading] = useState(true);
  const [formOpen, setFormOpen] = useState(false);
  const [form, setForm] = useState<FormState | null>(null);
  const [formError, setFormError] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [testing, setTesting] = useState<string | null>(null);
  const [testResult, setTestResult] = useState<Record<string, WebhookTestResult>>({});
  const [confirmDelete, setConfirmDelete] = useState<string | null>(null);
  const [guideOpen, setGuideOpen] = useState(false);

  const providerOf = useCallback(
    (type: WebhookType) => providers.find((p) => p.type === type),
    [providers]
  );

  const load = useCallback(async () => {
    setLoading(true);
    try {
      const res = await api.webhooks.list();
      setTargets(res.targets);
    } catch (err) {
      console.error("Failed to load webhook targets:", err);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    load();
  }, [load]);

  useEffect(() => {
    api.webhooks
      .providers()
      .then((res) => setProviders(res.providers))
      .catch(() => setProviders([]));
    api.alerts.rules
      .list()
      .then((res) => setRules(res.rules))
      .catch(() => setRules([]));
  }, []);

  const set = (patch: Partial<FormState>) =>
    setForm((prev) => (prev ? { ...prev, ...patch } : prev));

  const openCreate = () => {
    const first = providers[0];
    setForm({
      id: null,
      name: "",
      type: (first?.type as WebhookType) || "slack",
      url: "",
      secret: "",
      headerRows: [],
      replaceHeaders: true,
      config: defaultsFor(first),
      scopeAll: true,
      ruleIds: [],
      enabled: true,
    });
    setFormError(null);
    setFormOpen(true);
  };

  const openEdit = (target: WebhookTarget) => {
    const provider = providerOf(target.type);
    // Prefill non-secret config (region, chat_id, severity, …); leave secret
    // fields blank - they're redacted and re-entered only to change.
    const config: Record<string, string> = {};
    for (const f of provider?.fields || []) {
      if (f.secret) continue;
      const v = target.config?.[f.key];
      config[f.key] = v != null ? String(v) : (f.default ?? "");
    }
    setForm({
      id: target.id,
      name: target.name,
      type: target.type,
      url: "",
      secret: "",
      headerRows: [],
      replaceHeaders: false,
      config,
      scopeAll: !target.rule_ids || target.rule_ids.length === 0,
      ruleIds: target.rule_ids || [],
      enabled: target.enabled,
    });
    setFormError(null);
    setFormOpen(true);
  };

  const closeForm = () => {
    setFormOpen(false);
    setForm(null);
    setFormError(null);
  };

  const provider = form ? providerOf(form.type) : undefined;
  const isEdit = !!form?.id;
  const showUrl = !!provider && (provider.url_required || provider.has_default_url);
  const urlOptional = !!provider && !provider.url_required;

  const canSubmit = useMemo(() => {
    if (!form || !provider) return false;
    if (!form.name.trim()) return false;
    if (isEdit) return true; // server merge - existing values fill the gaps
    if (provider.url_required && !form.url.trim()) return false;
    for (const f of provider.fields) {
      if (f.required && f.type !== "enum" && !(form.config[f.key] || "").trim()) return false;
    }
    return true;
  }, [form, provider, isEdit]);

  const buildConfigObj = (): Record<string, string> | undefined => {
    if (!form || !provider || provider.fields.length === 0) return undefined;
    const out: Record<string, string> = {};
    for (const f of provider.fields) {
      const v = (form.config[f.key] ?? "").toString();
      if (f.type === "enum") {
        if (v) out[f.key] = v;
      } else if (v.trim()) {
        out[f.key] = v.trim();
      }
    }
    return out;
  };

  const buildHeaders = (): Record<string, string> => {
    if (!form) return {};
    const out: Record<string, string> = {};
    for (const r of form.headerRows) if (r.key.trim()) out[r.key.trim()] = r.value;
    return out;
  };

  const onSubmit = async () => {
    if (!form || !provider || saving || !canSubmit) return;
    setSaving(true);
    setFormError(null);
    try {
      const ruleIds = form.scopeAll ? [] : form.ruleIds;
      const config = buildConfigObj();
      const genericFamily = provider.supports_secret || provider.supports_headers;
      if (isEdit && form.id) {
        const patch: Parameters<typeof api.webhooks.update>[1] = {
          name: form.name.trim(),
          enabled: form.enabled,
          rule_ids: ruleIds,
        };
        if (form.url.trim()) patch.url = form.url.trim();
        if (config) patch.config = config;
        if (genericFamily && form.secret.trim()) patch.secret = form.secret.trim();
        if (provider.supports_headers && form.replaceHeaders) patch.headers = buildHeaders();
        await api.webhooks.update(form.id, patch);
      } else {
        await api.webhooks.create({
          name: form.name.trim(),
          type: form.type,
          url: form.url.trim() || undefined,
          enabled: form.enabled,
          secret: genericFamily && form.secret.trim() ? form.secret.trim() : undefined,
          headers: provider.supports_headers ? buildHeaders() : undefined,
          config,
          rule_ids: ruleIds.length ? ruleIds : undefined,
        });
      }
      closeForm();
      load();
    } catch (err) {
      setFormError(err instanceof Error ? err.message : String(err));
    } finally {
      setSaving(false);
    }
  };

  const onToggle = async (target: WebhookTarget) => {
    try {
      await api.webhooks.update(target.id, { enabled: !target.enabled });
      load();
    } catch (err) {
      console.error("Failed to toggle webhook:", err);
    }
  };

  const onDelete = async (id: string) => {
    try {
      await api.webhooks.remove(id);
      setConfirmDelete(null);
      load();
    } catch (err) {
      console.error("Failed to delete webhook:", err);
    }
  };

  const onTest = async (id: string) => {
    setTesting(id);
    setTestResult((prev) => {
      const next = { ...prev };
      delete next[id];
      return next;
    });
    try {
      const result = await api.webhooks.test(id);
      setTestResult((prev) => ({ ...prev, [id]: result }));
    } catch (err) {
      setTestResult((prev) => ({
        ...prev,
        [id]: { ok: false, status: null, attempts: 0, error: String(err) },
      }));
    } finally {
      setTesting(null);
      load();
    }
  };

  const labelOf = (type: WebhookType) => providerOf(type)?.label || type;

  return (
    <div className="card p-5 space-y-4">
      <div className="flex items-center justify-between gap-3">
        <div className="flex items-center gap-2 text-xs text-fg-muted">
          <Webhook className="w-3.5 h-3.5" />
          {t("webhooks.count", { count: targets.length })}
        </div>
        {!formOpen && (
          <button
            onClick={openCreate}
            disabled={providers.length === 0}
            className="btn-ghost border border-border inline-flex items-center gap-1.5 text-xs disabled:opacity-50"
          >
            <Plus className="w-3.5 h-3.5" />
            {t("webhooks.add")}
          </button>
        )}
      </div>

      {/* Target list */}
      {loading ? (
        <p className="text-xs text-fg-muted">{t("webhooks.loading")}</p>
      ) : targets.length === 0 && !formOpen ? (
        <div className="flex items-center gap-2 text-xs text-fg-muted py-2">
          <Webhook className="w-3.5 h-3.5" />
          {t("webhooks.empty")}
        </div>
      ) : (
        <div className="space-y-2">
          {targets.map((target) => {
            const result = testResult[target.id];
            return (
              <div
                key={target.id}
                className="bg-surface-2 border border-border rounded-lg px-3.5 py-3 space-y-2"
              >
                <div className="flex items-center gap-3 flex-wrap">
                  <span
                    className={`inline-flex items-center gap-1 text-[10px] font-semibold uppercase tracking-wide px-2 py-0.5 rounded border ${
                      TYPE_STYLES[target.type] || NEUTRAL_STYLE
                    }`}
                  >
                    {labelOf(target.type)}
                  </span>
                  <span className="text-sm text-fg-secondary font-medium">{target.name}</span>
                  <code className="text-[11px] text-fg-muted font-mono truncate max-w-[220px]">
                    {target.url_preview}
                  </code>
                  {target.rule_ids && target.rule_ids.length > 0 && (
                    <span className="text-[10px] text-status-warning/80">
                      {t("webhooks.scopedTo", { count: target.rule_ids.length })}
                    </span>
                  )}
                  <div className="ml-auto flex items-center gap-2">
                    {target.last_delivery && (
                      <span
                        title={target.last_delivery.error || undefined}
                        className={`inline-flex items-center gap-1 text-[10px] px-1.5 py-0.5 rounded-full ${
                          target.last_delivery.status === "success"
                            ? "text-status-success bg-status-success/10"
                            : "text-status-danger bg-status-danger/10"
                        }`}
                      >
                        {target.last_delivery.status === "success" ? (
                          <CheckCircle className="w-3 h-3" />
                        ) : (
                          <XCircle className="w-3 h-3" />
                        )}
                        {timeAgo(target.last_delivery.created_at)}
                      </span>
                    )}
                    <Toggle
                      checked={target.enabled}
                      onChange={() => onToggle(target)}
                      label={t("webhooks.enabled")}
                    />
                  </div>
                </div>

                <div className="flex items-center gap-2 flex-wrap">
                  <button
                    onClick={() => onTest(target.id)}
                    disabled={testing === target.id}
                    className="inline-flex items-center gap-1.5 text-[11px] px-2.5 py-1 rounded-md text-fg-secondary hover:text-fg-primary hover:bg-surface-4 border border-border transition-colors disabled:opacity-50"
                  >
                    {testing === target.id ? (
                      <Loader2 className="w-3 h-3 animate-spin" />
                    ) : (
                      <Zap className="w-3 h-3" />
                    )}
                    {t("webhooks.test")}
                  </button>
                  <button
                    onClick={() => openEdit(target)}
                    className="inline-flex items-center gap-1.5 text-[11px] px-2.5 py-1 rounded-md text-fg-secondary hover:text-fg-primary hover:bg-surface-4 border border-border transition-colors"
                  >
                    <Pencil className="w-3 h-3" />
                    {t("webhooks.edit")}
                  </button>
                  <button
                    onClick={() => setConfirmDelete(target.id)}
                    className="inline-flex items-center gap-1.5 text-[11px] px-2.5 py-1 rounded-md text-fg-muted hover:text-status-danger hover:bg-status-danger/10 border border-border transition-colors"
                  >
                    <Trash2 className="w-3 h-3" />
                    {t("webhooks.delete")}
                  </button>
                  {result && (
                    <span
                      className={`inline-flex items-center gap-1 text-[11px] ${
                        result.ok ? "text-status-success" : "text-status-danger"
                      }`}
                    >
                      {result.ok ? (
                        <CheckCircle className="w-3 h-3" />
                      ) : (
                        <AlertTriangle className="w-3 h-3" />
                      )}
                      {result.ok
                        ? t("webhooks.testOk", { status: result.status ?? 200 })
                        : t("webhooks.testFail", {
                            error: result.error || `HTTP ${result.status ?? "?"}`,
                          })}
                    </span>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}

      {/* Create / edit form */}
      {formOpen && form && provider && (
        <div className="border border-border rounded-lg p-4 space-y-3 bg-surface-1">
          <div className="flex items-center justify-between">
            <h4 className="text-xs font-semibold text-fg-secondary uppercase tracking-wide">
              {isEdit ? t("webhooks.editTitle") : t("webhooks.addTitle")}
            </h4>
            <button onClick={closeForm} className="text-fg-muted hover:text-fg-secondary">
              <X className="w-4 h-4" />
            </button>
          </div>

          <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
            <label className="block">
              <span className="text-[11px] text-fg-muted">{t("webhooks.fieldName")}</span>
              <input
                value={form.name}
                onChange={(e) => set({ name: e.target.value })}
                placeholder={t("webhooks.fieldNamePlaceholder")}
                className="input w-full mt-1 py-1.5 text-[11px] leading-normal"
              />
            </label>
            <label className="block">
              <span className="text-[11px] text-fg-muted">{t("webhooks.fieldType")}</span>
              <div className="mt-1">
                <Select<WebhookType>
                  value={form.type}
                  disabled={isEdit}
                  onChange={(type) =>
                    set({
                      type,
                      config: defaultsFor(providerOf(type)),
                      url: "",
                      secret: "",
                      headerRows: [],
                    })
                  }
                  options={providers.map((p) => ({ value: p.type, label: p.label }))}
                />
              </div>
            </label>
          </div>

          {/* URL (hidden for providers that derive their own URL) */}
          {showUrl && (
            <label className="block">
              <span className="text-[11px] text-fg-muted">
                {t("webhooks.fieldUrl")}
                {isEdit ? (
                  <span className="text-fg-muted"> - {t("webhooks.urlKeepHint")}</span>
                ) : urlOptional ? (
                  <span className="text-fg-muted"> - {t("webhooks.urlOptional")}</span>
                ) : null}
              </span>
              <input
                value={form.url}
                onChange={(e) => set({ url: e.target.value })}
                placeholder={
                  isEdit ? t("webhooks.urlKeepPlaceholder") : provider.url_hint || "https://…"
                }
                className="input w-full mt-1 py-1.5 text-[11px] leading-normal font-mono"
              />
            </label>
          )}
          {!showUrl && (
            <p className="text-[11px] text-fg-muted flex items-center gap-1.5">
              <Webhook className="w-3 h-3" />
              {t("webhooks.urlAuto")}
            </p>
          )}

          {/* Collapsible per-provider setup guide */}
          <div className="rounded-lg border border-border bg-surface-2/40 overflow-hidden">
            <button
              type="button"
              onClick={() => setGuideOpen((o) => !o)}
              className="w-full flex items-center justify-between gap-2 px-3 py-2 text-[11px] text-fg-secondary hover:bg-surface-3 transition-colors"
            >
              <span className="inline-flex items-center gap-1.5">
                <BookOpen className="w-3.5 h-3.5 text-fg-muted" />
                {t("webhooks.guideToggle", { provider: provider.label })}
              </span>
              <ChevronDown
                className={`w-3.5 h-3.5 text-fg-muted transition-transform ${guideOpen ? "rotate-180" : ""}`}
              />
            </button>
            {guideOpen && (
              <div className="px-3 pb-3 pt-2 space-y-2.5 border-t border-border">
                <ol className="list-decimal list-inside space-y-1 text-[11px] leading-relaxed text-fg-secondary marker:text-fg-muted">
                  {(t(`webhookGuides.${form.type}.steps`, { returnObjects: true }) as string[]).map(
                    (s, i) => (
                      <li key={i}>{s}</li>
                    )
                  )}
                </ol>
                {WEBHOOK_DOCS[form.type] && (
                  <a
                    href={WEBHOOK_DOCS[form.type]}
                    target="_blank"
                    rel="noopener noreferrer"
                    className="inline-flex items-center gap-1 text-[11px] text-accent hover:underline"
                  >
                    <ExternalLink className="w-3 h-3" />
                    {t("webhooks.guideDocs", { provider: provider.label })}
                  </a>
                )}
                <p className="flex items-start gap-1.5 text-[10px] text-fg-muted leading-relaxed pt-2 border-t border-border">
                  <Info className="w-3 h-3 mt-0.5 flex-shrink-0" />
                  {t("webhooks.guideStaleNote")}
                </p>
              </div>
            )}
          </div>

          {/* Provider-specific config fields */}
          {provider.fields.length > 0 && (
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3 pt-1 border-t border-border">
              {provider.fields.map((f) => (
                <label key={f.key} className="block">
                  <span className="text-[11px] text-fg-muted">
                    {f.label}
                    {f.required && <span className="text-status-danger"> *</span>}
                  </span>
                  {f.type === "enum" && f.options ? (
                    <div className="mt-1">
                      <Select
                        value={String(form.config[f.key] ?? f.default ?? "")}
                        onChange={(v) => set({ config: { ...form.config, [f.key]: v } })}
                        options={f.options.map((o) => ({ value: o, label: o }))}
                      />
                    </div>
                  ) : (
                    <input
                      type={f.secret ? "password" : "text"}
                      value={form.config[f.key] ?? ""}
                      onChange={(e) => set({ config: { ...form.config, [f.key]: e.target.value } })}
                      placeholder={f.secret && isEdit ? t("webhooks.secretKeepPlaceholder") : ""}
                      className="input w-full mt-1 py-1.5 text-[11px] leading-normal font-mono"
                    />
                  )}
                </label>
              ))}
            </div>
          )}

          {/* Generic family: HMAC secret + custom headers */}
          {provider.supports_secret && (
            <div className="space-y-3 pt-1 border-t border-border">
              <label className="block">
                <span className="text-[11px] text-fg-muted">{t("webhooks.fieldSecret")}</span>
                <input
                  type="password"
                  value={form.secret}
                  onChange={(e) => set({ secret: e.target.value })}
                  placeholder={
                    isEdit ? t("webhooks.secretKeepPlaceholder") : t("webhooks.secretPlaceholder")
                  }
                  className="input w-full mt-1 py-1.5 text-[11px] leading-normal font-mono"
                />
                <span className="text-[10px] text-fg-muted">{t("webhooks.secretHint")}</span>
              </label>

              <div>
                <div className="flex items-center justify-between">
                  <span className="text-[11px] text-fg-muted">{t("webhooks.fieldHeaders")}</span>
                  {isEdit && (
                    <Checkbox
                      checked={form.replaceHeaders}
                      onChange={(v) => set({ replaceHeaders: v })}
                      label={t("webhooks.replaceHeaders")}
                      labelClassName="text-[10px] text-fg-muted group-hover:text-fg-secondary"
                    />
                  )}
                </div>
                {(!isEdit || form.replaceHeaders) && (
                  <div className="space-y-1.5 mt-1.5">
                    {form.headerRows.map((row, i) => (
                      <div key={i} className="flex items-center gap-1.5">
                        <input
                          value={row.key}
                          onChange={(e) =>
                            set({
                              headerRows: form.headerRows.map((r, j) =>
                                j === i ? { ...r, key: e.target.value } : r
                              ),
                            })
                          }
                          placeholder={t("webhooks.headerKey")}
                          className="input flex-1 py-1.5 text-[11px] leading-normal font-mono"
                        />
                        <input
                          value={row.value}
                          onChange={(e) =>
                            set({
                              headerRows: form.headerRows.map((r, j) =>
                                j === i ? { ...r, value: e.target.value } : r
                              ),
                            })
                          }
                          placeholder={t("webhooks.headerValue")}
                          className="input flex-1 py-1.5 text-[11px] leading-normal font-mono"
                        />
                        <button
                          onClick={() =>
                            set({ headerRows: form.headerRows.filter((_, j) => j !== i) })
                          }
                          className="text-fg-muted hover:text-status-danger p-1"
                        >
                          <X className="w-3.5 h-3.5" />
                        </button>
                      </div>
                    ))}
                    <button
                      onClick={() =>
                        set({ headerRows: [...form.headerRows, { key: "", value: "" }] })
                      }
                      className="inline-flex items-center gap-1 text-[10px] text-fg-muted hover:text-fg-secondary"
                    >
                      <Plus className="w-3 h-3" />
                      {t("webhooks.addHeader")}
                    </button>
                  </div>
                )}
              </div>
            </div>
          )}

          {/* Optional per-rule scoping */}
          {rules.length > 0 && (
            <div className="pt-1 border-t border-border">
              <Checkbox
                checked={form.scopeAll}
                onChange={(v) => set({ scopeAll: v })}
                label={t("webhooks.scopeAll")}
                labelClassName="text-[11px] text-fg-secondary group-hover:text-fg-secondary"
              />
              {!form.scopeAll && (
                <div className="mt-2 grid grid-cols-1 sm:grid-cols-2 gap-1">
                  {rules.map((rule) => (
                    <Checkbox
                      key={rule.id}
                      checked={form.ruleIds.includes(rule.id)}
                      onChange={(checked) =>
                        set({
                          ruleIds: checked
                            ? [...form.ruleIds, rule.id]
                            : form.ruleIds.filter((id) => id !== rule.id),
                        })
                      }
                      label={rule.name}
                      labelClassName="text-[11px] text-fg-secondary group-hover:text-fg-secondary truncate"
                    />
                  ))}
                </div>
              )}
            </div>
          )}

          <div className="flex items-center gap-3 pt-1">
            <label className="inline-flex items-center gap-2 text-[11px] text-fg-secondary cursor-pointer">
              <Toggle checked={form.enabled} onChange={(v) => set({ enabled: v })} />
              {t("webhooks.enabledOnSave")}
            </label>
          </div>

          {formError && (
            <div className="flex items-center gap-1.5 text-[11px] text-status-danger">
              <AlertTriangle className="w-3.5 h-3.5" />
              {formError}
            </div>
          )}

          <div className="flex items-center gap-2 pt-1">
            <button
              onClick={onSubmit}
              disabled={!canSubmit || saving}
              className="btn-primary inline-flex items-center gap-1.5 text-xs disabled:opacity-50"
            >
              {saving ? (
                <Loader2 className="w-3.5 h-3.5 animate-spin" />
              ) : (
                <Check className="w-3.5 h-3.5" />
              )}
              {isEdit ? t("webhooks.save") : t("webhooks.create")}
            </button>
            <button onClick={closeForm} className="btn-ghost border border-border text-xs">
              {t("webhooks.cancel")}
            </button>
          </div>
        </div>
      )}

      <ConfirmModal
        open={!!confirmDelete}
        title={t("webhooks.deleteTitle")}
        message={t("webhooks.deleteMessage", {
          name: targets.find((x) => x.id === confirmDelete)?.name ?? "",
        })}
        confirmLabel={t("webhooks.delete")}
        cancelLabel={t("webhooks.cancel")}
        onCancel={() => setConfirmDelete(null)}
        onConfirm={() => confirmDelete && onDelete(confirmDelete)}
      />
    </div>
  );
}
