"use client"

import * as React from "react"
import { toast } from "sonner"

import {
  createAssetTypeDetectRuleClient,
  deleteAssetTypeDetectRuleClient,
  listAssetTypeDetectClasses,
  listAssetTypeDetectRules,
  previewAssetTypeDetectClient,
  reorderAssetTypeDetectRulesClient,
  saveAssetTypeDetectClass,
  updateAssetTypeDetectRuleClient,
  type AssetTypeDetectClassRow,
  type AssetTypeDetectRuleRow,
} from "@/components/xml-apis/asset-type-detect/api/asset-type-detect.api"
import {
  UNMAPPED_MACRO_LABEL,
  toRuleOperator,
  type RuleOperator,
} from "@/components/xml-apis/asset-type-detect/types"
import { idsAfterMove, positionInClass } from "@/components/xml-apis/shared/detect-rule-order"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
import { Switch } from "@/components/ui/switch"
import { useAnyChanged } from "@/hooks/use-changed"
import { cn } from "@/lib/utils"

const ASSET_CLASS_LABELS: Record<string, string> = {
  bond: "Bond",
  structure: "Structure",
  equity: "Equity",
  bond_fund: "Bond Fund",
  equity_fund: "Equity Fund",
  mixed_fund: "Mixed Fund",
  cash: "Cash",
  call: "Call",
  fd: "Fixed Deposit",
  od: "Overdraft",
  loan: "Loan",
  commodity: "Commodity",
  crypto: "Crypto",
  interest: "Interest / Dividend",
  alternative_investment: "Alternative Investment",
  equity_accumulator: "Equity Accumulator",
  equity_option: "Equity Option",
}

function formatClassLabel(assetClass: string): string {
  return (
    ASSET_CLASS_LABELS[assetClass] ??
    assetClass
      .split("_")
      .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
      .join(" ")
  )
}

const ALL_RULES_VIEW = "__all__"
const MACRO_VIEW_PREFIX = "__macro__:"

function macroViewKey(macroName: string): string {
  return `${MACRO_VIEW_PREFIX}${macroName}`
}

function parseMacroView(selection: string): string | null {
  return selection.startsWith(MACRO_VIEW_PREFIX)
    ? selection.slice(MACRO_VIEW_PREFIX.length)
    : null
}

export function AssetTypeDetectPage() {
  const [classes, setClasses] = React.useState<AssetTypeDetectClassRow[]>([])
  const [macroOrder, setMacroOrder] = React.useState<string[]>([])
  const [selectedClass, setSelectedClass] = React.useState<string>(ALL_RULES_VIEW)
  const [rules, setRules] = React.useState<AssetTypeDetectRuleRow[]>([])
  const [allRules, setAllRules] = React.useState<AssetTypeDetectRuleRow[]>([])
  const [allRulesFilter, setAllRulesFilter] = React.useState("")
  const [macroDefaultDrafts, setMacroDefaultDrafts] = React.useState<
    Record<string, string>
  >({})
  const [showDefaults, setShowDefaults] = React.useState(false)
  const [showAdvancedAc, setShowAdvancedAc] = React.useState(false)
  const [defaultAt, setDefaultAt] = React.useState("")
  const [loading, setLoading] = React.useState(true)
  const [savingClass, setSavingClass] = React.useState(false)
  const [previewName, setPreviewName] = React.useState("")
  const [previewClass, setPreviewClass] = React.useState("")
  const [previewResult, setPreviewResult] = React.useState<string>("")

  const [newRuleMacro, setNewRuleMacro] = React.useState("")
  const [newValue, setNewValue] = React.useState("")
  const [newOperator, setNewOperator] = React.useState<RuleOperator>("contains")
  const [newThenType, setNewThenType] = React.useState("")
  const [addingRule, setAddingRule] = React.useState(false)

  const [editingId, setEditingId] = React.useState<number | null>(null)
  const [editOperator, setEditOperator] = React.useState<RuleOperator>("contains")
  const [editValue, setEditValue] = React.useState("")
  const [editThenType, setEditThenType] = React.useState("")
  const [savingEdit, setSavingEdit] = React.useState(false)

  const isAllView = selectedClass === ALL_RULES_VIEW
  const selectedMacroName = parseMacroView(selectedClass)
  const isMacroView = selectedMacroName !== null
  const selected =
    !isAllView && !isMacroView
      ? (classes.find((c) => c.asset_class === selectedClass) ?? null)
      : null

  const reloadClasses = React.useCallback(async () => {
    const result = await listAssetTypeDetectClasses()
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    setClasses(result.classes)
    setMacroOrder(result.macroOrder ?? [])
    const byMacro = new Map<string, string[]>()
    for (const c of result.classes) {
      const macro = c.macro_asset_class
      const list = byMacro.get(macro) ?? []
      list.push((c.default_asset_type || "").trim())
      byMacro.set(macro, list)
    }
    setMacroDefaultDrafts(
      Object.fromEntries(
        [...byMacro.entries()].map(([macro, vals]) => {
          const first = vals[0] ?? ""
          return [macro, vals.every((v) => v === first) ? first : first]
        }),
      ),
    )
    setNewRuleMacro((prev) => prev || result.macroOrder?.[0] || "")
    setPreviewClass((prev) => prev || result.classes[0]?.asset_class || "")
    setSelectedClass((prev) => {
      if (prev === ALL_RULES_VIEW) return prev
      if (parseMacroView(prev)) return prev
      if (prev && result.classes.some((c) => c.asset_class === prev)) return prev
      return ALL_RULES_VIEW
    })
  }, [])

  // "Apply" functions take an already-fetched result, so an effect can call them from a promise
  // callback (not synchronously); the reload functions are the same thing for event handlers.
  const applyRules = React.useCallback(
    (result: Awaited<ReturnType<typeof listAssetTypeDetectRules>>) => {
      if (result.errorMessage) {
        toast.error(result.errorMessage)
        return
      }
      setRules(result.rules)
    },
    [],
  )

  const applyAllRules = React.useCallback(
    (result: Awaited<ReturnType<typeof listAssetTypeDetectRules>>) => {
      if (result.errorMessage) {
        toast.error(result.errorMessage)
        return
      }
      setAllRules(result.rules)
    },
    [],
  )

  const reloadRules = React.useCallback(
    async (assetClass: string) => applyRules(await listAssetTypeDetectRules(assetClass)),
    [applyRules],
  )

  const reloadAllRules = React.useCallback(
    async () => applyAllRules(await listAssetTypeDetectRules()),
    [applyAllRules],
  )

  const refreshRules = React.useCallback(async () => {
    if (isAllView || isMacroView) {
      await reloadAllRules()
      return
    }
    if (!selectedClass) {
      setRules([])
      return
    }
    await reloadRules(selectedClass)
  }, [isAllView, isMacroView, reloadAllRules, reloadRules, selectedClass])

  React.useEffect(() => {
    let cancelled = false
    ;(async () => {
      await reloadClasses()
      if (!cancelled) setLoading(false)
    })()
    return () => {
      cancelled = true
    }
  }, [reloadClasses])

  if (useAnyChanged([selectedClass, classes])) {
    // Switching view (or reloading the classes) abandons any inline rule edit.
    setEditingId(null)
    const macroName = parseMacroView(selectedClass)
    if (macroName) {
      setNewRuleMacro(macroName)
      const macroClasses = classes.filter(
        (c) => c.macro_asset_class === macroName,
      )
      setDefaultAt(macroClasses[0]?.default_asset_type ?? "")
      const first = macroClasses[0]?.asset_class ?? ""
      if (first) {
        setPreviewClass(first)
      }
    } else if (selectedClass && selectedClass !== ALL_RULES_VIEW) {
      const cls = classes.find((c) => c.asset_class === selectedClass)
      const macro = cls?.macro_asset_class ?? UNMAPPED_MACRO_LABEL
      const siblings = classes.filter((c) => c.macro_asset_class === macro)
      setDefaultAt(siblings[0]?.default_asset_type ?? cls?.default_asset_type ?? "")
      setNewRuleMacro(macro)
      setPreviewClass(selectedClass)
    }
  }

  React.useEffect(() => {
    if (!selectedClass) return
    if (selectedClass === ALL_RULES_VIEW || parseMacroView(selectedClass)) {
      void listAssetTypeDetectRules().then(applyAllRules)
      return
    }
    void listAssetTypeDetectRules(selectedClass).then(applyRules)
  }, [selectedClass, classes, applyRules, applyAllRules])

  const macroForAssetClass = React.useCallback(
    (assetClass: string) => {
      const row = classes.find((c) => c.asset_class === assetClass)
      return row?.macro_asset_class ?? UNMAPPED_MACRO_LABEL
    },
    [classes],
  )

  const classesByMacro = React.useMemo(() => {
    const buckets = new Map<string, AssetTypeDetectClassRow[]>()
    for (const row of classes) {
      const macro = row.macro_asset_class
      const list = buckets.get(macro) ?? []
      list.push(row)
      buckets.set(macro, list)
    }
    for (const list of buckets.values()) {
      list.sort((a, b) =>
        formatClassLabel(a.asset_class).localeCompare(formatClassLabel(b.asset_class)),
      )
    }
    const known = macroOrder.filter((name) => buckets.has(name))
    const rest = Array.from(buckets.keys())
      .filter((name) => !known.includes(name))
      .sort((a, b) => {
        if (a === UNMAPPED_MACRO_LABEL) return 1
        if (b === UNMAPPED_MACRO_LABEL) return -1
        return a.localeCompare(b)
      })
    return [...known, ...rest].map((name) => ({
      name,
      classes: buckets.get(name) ?? [],
    }))
  }, [classes, macroOrder])

  const activeMacroName =
    selectedMacroName ??
    (selected ? macroForAssetClass(selected.asset_class) : null)

  const macroGroupClasses = React.useMemo(() => {
    if (!selectedMacroName) return [] as AssetTypeDetectClassRow[]
    return classesByMacro.find((g) => g.name === selectedMacroName)?.classes ?? []
  }, [classesByMacro, selectedMacroName])

  const filteredAllRules = React.useMemo(() => {
    const q = allRulesFilter.trim().toLowerCase()
    const macroRank = new Map(macroOrder.map((name, index) => [name, index]))
    const sorted = [...allRules].sort((a, b) => {
      const macroA = macroForAssetClass(a.asset_class)
      const macroB = macroForAssetClass(b.asset_class)
      const rankA = macroRank.get(macroA) ?? (macroA === UNMAPPED_MACRO_LABEL ? 9999 : 5000)
      const rankB = macroRank.get(macroB) ?? (macroB === UNMAPPED_MACRO_LABEL ? 9999 : 5000)
      if (rankA !== rankB) return rankA - rankB
      if (macroA !== macroB) return macroA.localeCompare(macroB)
      const classCmp = formatClassLabel(a.asset_class).localeCompare(
        formatClassLabel(b.asset_class),
      )
      if (classCmp !== 0) return classCmp
      if (a.sort_order !== b.sort_order) return a.sort_order - b.sort_order
      return a.id - b.id
    })
    if (!q) return sorted
    return sorted.filter((rule) => {
      const hay = [
        macroForAssetClass(rule.asset_class),
        formatClassLabel(rule.asset_class),
        rule.asset_class,
        rule.operator,
        rule.value ?? "",
        rule.then_asset_type,
        rule.label ?? "",
      ]
        .join(" ")
        .toLowerCase()
      return hay.includes(q)
    })
  }, [allRules, allRulesFilter, macroForAssetClass, macroOrder])

  const filteredMacroRules = React.useMemo(() => {
    if (!selectedMacroName) return [] as AssetTypeDetectRuleRow[]
    const allowed = new Set(macroGroupClasses.map((c) => c.asset_class))
    return filteredAllRules.filter((rule) => allowed.has(rule.asset_class))
  }, [selectedMacroName, macroGroupClasses, filteredAllRules])

  const newRuleTargetMacro = isMacroView ? selectedMacroName : newRuleMacro

  const newRuleTargetClasses = React.useMemo(() => {
    if (isAllView || isMacroView) {
      if (!newRuleTargetMacro) return [] as AssetTypeDetectClassRow[]
      return classesByMacro.find((g) => g.name === newRuleTargetMacro)?.classes ?? []
    }
    if (!selected) return [] as AssetTypeDetectClassRow[]
    return [selected]
  }, [isAllView, isMacroView, newRuleTargetMacro, classesByMacro, selected])

  async function onSaveMacroDefault(macroName: string, value: string) {
    const targets =
      classesByMacro.find((g) => g.name === macroName)?.classes ?? []
    if (targets.length === 0) {
      toast.error("No asset classes under this Macro")
      return
    }
    const trimmed = value.trim()
    if (!trimmed) {
      toast.error("Common Asset Type is required")
      return
    }
    setSavingClass(true)
    let saved = 0
    let lastError: string | null = null
    for (const target of targets) {
      const result = await saveAssetTypeDetectClass({
        id: target.id,
        default_asset_type: trimmed,
      })
      if (result.errorMessage) {
        lastError = result.errorMessage
        continue
      }
      saved += 1
    }
    setSavingClass(false)
    if (saved === 0) {
      toast.error(lastError ?? "Save failed")
      return
    }
    setMacroDefaultDrafts((prev) => ({ ...prev, [macroName]: trimmed }))
    setDefaultAt(trimmed)
    toast.success(
      `Common AT saved for ${saved}/${targets.length} classes under ${macroName}`,
    )
    await reloadClasses()
  }

  async function onSaveDefault() {
    const macroName = selectedMacroName ?? (selected ? macroForAssetClass(selected.asset_class) : null)
    if (!macroName) return
    await onSaveMacroDefault(macroName, defaultAt)
  }

  async function onToggleEnabled(enabled: boolean, assetClass?: string) {
    const targetClass = assetClass
      ? classes.find((c) => c.asset_class === assetClass)
      : selected
    if (!targetClass) return
    const result = await saveAssetTypeDetectClass({ id: targetClass.id, enabled })
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    toast.success(enabled ? "Class enabled" : "Class disabled")
    await reloadClasses()
  }

  async function onAddRule() {
    if (!newValue.trim() || !newThenType.trim()) {
      toast.error("Value and Asset Type are required")
      return
    }
    const targets = newRuleTargetClasses
    if (targets.length === 0) {
      toast.error(
        isAllView || isMacroView
          ? "Pick a Macro that has asset classes"
          : "Select an asset class",
      )
      return
    }
    setAddingRule(true)
    const value = newValue.trim()
    const thenType = newThenType.trim()
    const label = `${newOperator} ${value}`
    let created = 0
    let lastError: string | null = null
    for (const target of targets) {
      const result = await createAssetTypeDetectRuleClient({
        asset_class: target.asset_class,
        field: "name",
        operator: newOperator,
        value,
        then_asset_type: thenType,
        enabled: true,
        flag_iam: false,
        label,
      })
      if (result.errorMessage || !result.rule) {
        lastError = result.errorMessage ?? "Create failed"
        continue
      }
      created += 1
    }
    setAddingRule(false)
    if (created === 0) {
      toast.error(lastError ?? "Create failed")
      return
    }
    toast.success(
      targets.length === 1
        ? "Rule created"
        : `Rule created for ${created}/${targets.length} classes under ${newRuleTargetMacro}`,
    )
    setNewValue("")
    await refreshRules()
  }

  async function onDeleteRule(id: number) {
    const result = await deleteAssetTypeDetectRuleClient(id)
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    if (editingId === id) {
      setEditingId(null)
    }
    toast.success("Rule deleted")
    await refreshRules()
  }

  function onStartEdit(rule: AssetTypeDetectRuleRow) {
    setEditingId(rule.id)
    setEditOperator(toRuleOperator(rule.operator))
    setEditValue(rule.value ?? "")
    setEditThenType(rule.then_asset_type)
  }

  function onCancelEdit() {
    setEditingId(null)
    setEditValue("")
    setEditThenType("")
  }

  async function onSaveEdit() {
    if (editingId == null) return
    if (!editValue.trim() || !editThenType.trim()) {
      toast.error("Value and Asset Type are required")
      return
    }
    setSavingEdit(true)
    try {
      const result = await updateAssetTypeDetectRuleClient({
        id: editingId,
        operator: editOperator,
        value: editValue.trim(),
        then_asset_type: editThenType.trim(),
        label: `${editOperator} ${editValue.trim()}`,
      })
      if (result.errorMessage || !result.rule) {
        toast.error(result.errorMessage ?? "Update failed")
        return
      }
      toast.success("Rule updated")
      setEditingId(null)
      await refreshRules()
    } finally {
      setSavingEdit(false)
    }
  }

  async function onMove(id: number, direction: -1 | 1, assetClass: string) {
    const ordered = idsAfterMove(isAllView || isMacroView ? allRules : rules, id, direction, assetClass)
    if (!ordered) return
    const result = await reorderAssetTypeDetectRulesClient(assetClass, ordered)
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    if (isAllView || isMacroView) {
      await reloadAllRules()
    } else {
      setRules(result.rules)
    }
  }

  async function onToggleRule(rule: AssetTypeDetectRuleRow) {
    const result = await updateAssetTypeDetectRuleClient({
      id: rule.id,
      enabled: !rule.enabled,
    })
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    await refreshRules()
  }

  async function onToggleFlagIam(rule: AssetTypeDetectRuleRow) {
    const result = await updateAssetTypeDetectRuleClient({
      id: rule.id,
      flag_iam: !rule.flag_iam,
    })
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    toast.success(rule.flag_iam ? "Flag IAM off" : "Flag IAM on")
    await refreshRules()
  }

  async function onPreview() {
    const classForPreview =
      isAllView || isMacroView ? previewClass : selectedClass
    if (!classForPreview || !previewName.trim()) {
      toast.error("Enter a sample security name")
      return
    }
    const result = await previewAssetTypeDetectClient({
      rows: [{ name: previewName.trim() }],
      classVerdicts: [classForPreview],
    })
    if (result.errorMessage) {
      toast.error(result.errorMessage)
      return
    }
    const v = result.verdicts[0]
    if (!v) {
      setPreviewResult("No verdict")
      return
    }
    const via =
      v.via === "rule"
        ? `rule (${v.matchedOperator} "${v.matchedValue}")`
        : v.via === "default"
          ? "Default"
          : v.via
    const flagNote = v.flagIam ? " · Flag IAM" : ""
    setPreviewResult(`${v.assetType ?? "—"} via ${via}${flagNote}`)
  }

  if (loading) {
    return <div className="p-6 text-sm text-muted-foreground">Loading asset type detect…</div>
  }

  function renderAddRuleForm(mode: "all" | "macro" | "class") {
    const showMacroSelect = mode === "all"
    const targetCount = newRuleTargetClasses.length
    const macroLabel =
      mode === "macro"
        ? selectedMacroName
        : mode === "all"
          ? newRuleMacro
          : null

    return (
      <div className="space-y-2 rounded-md border p-3">
        <div
          className={cn(
            "grid gap-2",
            showMacroSelect ? "md:grid-cols-5" : "md:grid-cols-4",
          )}
        >
          {showMacroSelect ? (
            <div className="space-y-1">
              <Label>Macro</Label>
              <Select value={newRuleMacro} onValueChange={setNewRuleMacro}>
                <SelectTrigger>
                  <SelectValue placeholder="Macro" />
                </SelectTrigger>
                <SelectContent>
                  {classesByMacro.map((group) => (
                    <SelectItem key={group.name} value={group.name}>
                      {group.name}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          ) : null}
          <div className="space-y-1">
            <Label>Operator</Label>
            <Select
              value={newOperator}
              onValueChange={(v) => setNewOperator(toRuleOperator(v))}
              disabled={addingRule}
            >
              <SelectTrigger>
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="contains">contains</SelectItem>
                <SelectItem value="regex">regex</SelectItem>
              </SelectContent>
            </Select>
          </div>
          <div className="space-y-1">
            <Label>Name value</Label>
            <Input
              value={newValue}
              onChange={(e) => setNewValue(e.target.value)}
              disabled={addingRule}
              placeholder={
                newOperator === "regex" ? "e.g. ^\\d+(?:\\.\\d+)?%" : "e.g. ETF"
              }
            />
          </div>
          <div className="space-y-1">
            <Label>Then Asset Type</Label>
            <Input
              value={newThenType}
              onChange={(e) => setNewThenType(e.target.value)}
              disabled={addingRule}
              placeholder="e.g. Equity Fund"
            />
          </div>
          <div className="flex items-end">
            <Button
              className="w-full"
              onClick={() => void onAddRule()}
              disabled={addingRule}
            >
              {addingRule ? "Adding…" : "Add rule"}
            </Button>
          </div>
        </div>
        {mode !== "class" && macroLabel ? (
          <p className="text-xs text-muted-foreground">
            Creates the same rule for every Asset Class under {macroLabel}
            {targetCount > 0 ? ` (${targetCount})` : ""}.
          </p>
        ) : null}
      </div>
    )
  }

  function renderRulesTable(
    rows: AssetTypeDetectRuleRow[],
    options: { showMacro: boolean },
  ) {
    const showAc = options.showMacro && showAdvancedAc
    const classOrderIndex = (rule: AssetTypeDetectRuleRow) =>
      positionInClass(options.showMacro ? allRules : rules, rule)
    const colSpan = 8 + (options.showMacro ? 1 : 0) + (showAc ? 1 : 0)

    return (
      <div className="overflow-hidden rounded-md border">
        <table className="w-full text-sm">
          <thead className="bg-muted/50 text-left">
            <tr>
              {options.showMacro ? <th className="px-3 py-2">Macro</th> : null}
              {showAc ? <th className="px-3 py-2">Asset Class</th> : null}
              <th className="px-3 py-2">#</th>
              <th className="px-3 py-2">If name</th>
              <th className="px-3 py-2">Value</th>
              <th className="px-3 py-2">Then Asset Type</th>
              <th className="px-3 py-2">On</th>
              <th className="px-3 py-2" title="When this rule matches on common_auto, flag the ISIN Asset Master">
                Flag IAM
              </th>
              <th className="px-3 py-2">Order</th>
              <th className="px-3 py-2" />
            </tr>
          </thead>
          <tbody>
            {rows.length === 0 ? (
              <tr>
                <td
                  colSpan={colSpan}
                  className="px-3 py-4 text-muted-foreground"
                >
                  No rules yet — add one above. Until then every row uses Default.
                </td>
              </tr>
            ) : (
              rows.map((rule) => {
                const { index, total } = classOrderIndex(rule)
                const isEditing = editingId === rule.id
                return (
                  <tr key={rule.id} className="border-t">
                    {options.showMacro ? (
                      <td className="px-3 py-2 text-xs text-muted-foreground">
                        {macroForAssetClass(rule.asset_class)}
                      </td>
                    ) : null}
                    {showAc ? (
                      <td className="px-3 py-2">
                        {formatClassLabel(rule.asset_class)}
                      </td>
                    ) : null}
                    <td className="px-3 py-2">{index + 1}</td>
                    <td className="px-3 py-2 font-mono text-xs">
                      {isEditing ? (
                        <Select
                          value={editOperator}
                          onValueChange={(v) =>
                            setEditOperator(toRuleOperator(v))
                          }
                          disabled={savingEdit}
                        >
                          <SelectTrigger className="h-8 w-[7.5rem]">
                            <SelectValue />
                          </SelectTrigger>
                          <SelectContent>
                            <SelectItem value="contains">contains</SelectItem>
                            <SelectItem value="regex">regex</SelectItem>
                          </SelectContent>
                        </Select>
                      ) : (
                        rule.operator
                      )}
                    </td>
                    <td className="px-3 py-2 font-mono text-xs">
                      {isEditing ? (
                        <Input
                          className="h-8 min-w-[10rem] font-mono text-xs"
                          value={editValue}
                          disabled={savingEdit}
                          onChange={(e) => setEditValue(e.target.value)}
                          onKeyDown={(e) => {
                            if (e.key === "Enter") void onSaveEdit()
                            if (e.key === "Escape") onCancelEdit()
                          }}
                        />
                      ) : (
                        rule.value
                      )}
                    </td>
                    <td className="px-3 py-2">
                      {isEditing ? (
                        <Input
                          className="h-8 min-w-[8rem] text-sm"
                          value={editThenType}
                          disabled={savingEdit}
                          onChange={(e) => setEditThenType(e.target.value)}
                          onKeyDown={(e) => {
                            if (e.key === "Enter") void onSaveEdit()
                            if (e.key === "Escape") onCancelEdit()
                          }}
                        />
                      ) : (
                        rule.then_asset_type
                      )}
                    </td>
                    <td className="px-3 py-2">
                      <Switch
                        checked={rule.enabled}
                        disabled={isEditing}
                        onCheckedChange={() => void onToggleRule(rule)}
                      />
                    </td>
                    <td className="px-3 py-2">
                      <Switch
                        checked={Boolean(rule.flag_iam)}
                        disabled={isEditing}
                        onCheckedChange={() => void onToggleFlagIam(rule)}
                      />
                    </td>
                    <td className="px-3 py-2">
                      <div className="flex gap-1">
                        <Button
                          type="button"
                          size="sm"
                          variant="outline"
                          disabled={isEditing || index <= 0}
                          onClick={() => void onMove(rule.id, -1, rule.asset_class)}
                        >
                          Up
                        </Button>
                        <Button
                          type="button"
                          size="sm"
                          variant="outline"
                          disabled={isEditing || index < 0 || index >= total - 1}
                          onClick={() => void onMove(rule.id, 1, rule.asset_class)}
                        >
                          Down
                        </Button>
                      </div>
                    </td>
                    <td className="px-3 py-2">
                      {isEditing ? (
                        <div className="flex gap-1">
                          <Button
                            type="button"
                            size="sm"
                            disabled={savingEdit}
                            onClick={() => void onSaveEdit()}
                          >
                            Save
                          </Button>
                          <Button
                            type="button"
                            size="sm"
                            variant="ghost"
                            disabled={savingEdit}
                            onClick={onCancelEdit}
                          >
                            Cancel
                          </Button>
                        </div>
                      ) : (
                        <div className="flex gap-1">
                          <Button
                            type="button"
                            size="sm"
                            variant="ghost"
                            disabled={editingId != null}
                            onClick={() => onStartEdit(rule)}
                          >
                            Edit
                          </Button>
                          <Button
                            type="button"
                            size="sm"
                            variant="ghost"
                            disabled={editingId != null}
                            onClick={() => void onDeleteRule(rule.id)}
                          >
                            Delete
                          </Button>
                        </div>
                      )}
                    </td>
                  </tr>
                )
              })
            )}
          </tbody>
        </table>
      </div>
    )
  }

  return (
    <div className="flex h-full min-h-[70vh] gap-4 p-4">
      <aside className="w-56 shrink-0 space-y-2 border-r pr-3">
        <h2 className="text-sm font-semibold">Views</h2>
        <div className="space-y-1">
          <button
            type="button"
            onClick={() => setSelectedClass(ALL_RULES_VIEW)}
            className={cn(
              "w-full rounded-md px-2 py-1.5 text-left text-sm font-medium",
              isAllView ? "bg-primary text-primary-foreground" : "hover:bg-muted",
            )}
          >
            All Rules
          </button>
          <div className="my-2 border-t" />
          <p className="px-1 text-xs text-muted-foreground">Macros</p>
          {classesByMacro.map((group) => {
            const isMacroActive = activeMacroName === group.name
            return (
              <button
                key={group.name}
                type="button"
                onClick={() => setSelectedClass(macroViewKey(group.name))}
                className={cn(
                  "w-full rounded-md px-2 py-1.5 text-left text-sm font-medium",
                  isMacroActive
                    ? "bg-primary text-primary-foreground"
                    : "hover:bg-muted",
                )}
              >
                {group.name}
                <span
                  className={cn(
                    "ml-1 text-[10px] font-normal",
                    isMacroActive ? "text-primary-foreground/80" : "text-muted-foreground",
                  )}
                >
                  ({group.classes.length})
                </span>
              </button>
            )
          })}
        </div>
      </aside>

      <main className="min-w-0 flex-1 space-y-6">
        {isAllView ? (
          <div className="space-y-4">
            <div className="space-y-2">
              <h1 className="text-xl font-semibold">All Rules</h1>
              <p className="text-sm text-muted-foreground">
                Add, edit, toggle, reorder, and delete rules here. Defaults fall back when no
                rule matches.
              </p>
            </div>

            {renderAddRuleForm("all")}

            <div className="flex flex-wrap items-center gap-2">
              <Input
                className="max-w-sm"
                placeholder="Filter by class, value, or asset type…"
                value={allRulesFilter}
                onChange={(e) => setAllRulesFilter(e.target.value)}
              />
              <span className="text-xs text-muted-foreground">
                {filteredAllRules.length} rule{filteredAllRules.length === 1 ? "" : "s"}
              </span>
              <Button
                type="button"
                size="sm"
                variant={showAdvancedAc ? "secondary" : "outline"}
                onClick={() => setShowAdvancedAc((v) => !v)}
              >
                {showAdvancedAc ? "Hide Advanced" : "Advanced"}
              </Button>
            </div>

            {renderRulesTable(filteredAllRules, { showMacro: true })}

            <section className="space-y-2 rounded-md border p-3">
              <div className="flex flex-wrap items-center justify-between gap-2">
                <div>
                  <h2 className="text-sm font-semibold">Macro common AT</h2>
                  <p className="text-xs text-muted-foreground">
                    Shared Default Asset Type for every class under the Macro when no
                    name rule matches.
                  </p>
                </div>
                <Button
                  type="button"
                  size="sm"
                  variant="outline"
                  onClick={() => setShowDefaults((v) => !v)}
                >
                  {showDefaults ? "Hide defaults" : "Edit defaults"}
                </Button>
              </div>
              {showDefaults ? (
                <div className="overflow-hidden rounded-md border">
                  <table className="w-full text-sm">
                    <thead className="bg-muted/40 text-left">
                      <tr>
                        <th className="px-3 py-2">Macro</th>
                        <th className="px-3 py-2">Classes</th>
                        <th className="px-3 py-2">Common Asset Type</th>
                        <th className="px-3 py-2" />
                      </tr>
                    </thead>
                    <tbody>
                      {classesByMacro.map((group) => {
                        const sharedStored = group.classes[0]?.default_asset_type ?? ""
                        const allSame = group.classes.every(
                          (c) => c.default_asset_type === sharedStored,
                        )
                        return (
                          <tr key={group.name} className="border-t">
                            <td className="px-3 py-2 font-medium">{group.name}</td>
                            <td className="px-3 py-2 text-xs text-muted-foreground">
                              {group.classes.length}
                              {!allSame ? " · mixed" : ""}
                            </td>
                            <td className="px-3 py-2">
                              <Input
                                value={macroDefaultDrafts[group.name] ?? sharedStored}
                                onChange={(e) =>
                                  setMacroDefaultDrafts((prev) => ({
                                    ...prev,
                                    [group.name]: e.target.value,
                                  }))
                                }
                                onBlur={() => {
                                  const draft = (
                                    macroDefaultDrafts[group.name] ?? sharedStored
                                  ).trim()
                                  if (draft && (draft !== sharedStored || !allSame)) {
                                    void onSaveMacroDefault(group.name, draft)
                                  }
                                }}
                              />
                            </td>
                            <td className="px-3 py-2">
                              <Button
                                type="button"
                                size="sm"
                                variant="ghost"
                                disabled={savingClass}
                                onClick={() =>
                                  void onSaveMacroDefault(
                                    group.name,
                                    macroDefaultDrafts[group.name] ?? sharedStored,
                                  )
                                }
                              >
                                Save
                              </Button>
                            </td>
                          </tr>
                        )
                      })}
                    </tbody>
                  </table>
                </div>
              ) : null}
            </section>

            <section className="space-y-2 rounded-md border p-3">
              <h2 className="text-sm font-semibold">Preview sample name</h2>
              <div className="flex flex-wrap gap-2">
                <Select value={previewClass} onValueChange={setPreviewClass}>
                  <SelectTrigger className="w-[220px]">
                    <SelectValue placeholder="Asset class" />
                  </SelectTrigger>
                  <SelectContent>
                    {classes.map((c) => (
                      <SelectItem key={c.asset_class} value={c.asset_class}>
                        {formatClassLabel(c.asset_class)}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input
                  className="min-w-[280px] flex-1"
                  placeholder="e.g. VANGUARD S&P 500 ETF"
                  value={previewName}
                  onChange={(e) => setPreviewName(e.target.value)}
                />
                <Button variant="secondary" onClick={() => void onPreview()}>
                  Preview
                </Button>
              </div>
              {previewResult ? (
                <p className="text-sm">
                  Result: <span className="font-medium">{previewResult}</span>
                </p>
              ) : null}
            </section>
          </div>
        ) : isMacroView && selectedMacroName ? (
          <div className="space-y-4">
            <div className="space-y-3">
              <div className="space-y-2">
                <h1 className="text-xl font-semibold">{selectedMacroName}</h1>
                <p className="text-sm text-muted-foreground">
                  Rules apply across every Asset Class under this Macro. Common AT is
                  used when no name rule matches.
                </p>
              </div>
              <div className="flex flex-wrap items-end gap-2">
                <div className="min-w-[240px] flex-1 space-y-1">
                  <Label htmlFor="macro-common-at">Common Asset Type</Label>
                  <Input
                    id="macro-common-at"
                    value={defaultAt}
                    onChange={(e) => setDefaultAt(e.target.value)}
                    disabled={savingClass}
                  />
                </div>
                <Button
                  onClick={() => void onSaveDefault()}
                  disabled={savingClass}
                >
                  {savingClass ? "Saving…" : "Save Common AT"}
                </Button>
              </div>
              <p className="text-xs text-muted-foreground">
                else → {defaultAt || "Common AT"} · applies to{" "}
                {macroGroupClasses.length} class
                {macroGroupClasses.length === 1 ? "" : "es"}
              </p>
            </div>

            {renderAddRuleForm("macro")}

            <div className="flex flex-wrap items-center gap-2">
              <Input
                className="max-w-sm"
                placeholder="Filter by class, value, or asset type…"
                value={allRulesFilter}
                onChange={(e) => setAllRulesFilter(e.target.value)}
              />
              <span className="text-xs text-muted-foreground">
                {filteredMacroRules.length} rule
                {filteredMacroRules.length === 1 ? "" : "s"}
              </span>
              <Button
                type="button"
                size="sm"
                variant={showAdvancedAc ? "secondary" : "outline"}
                onClick={() => setShowAdvancedAc((v) => !v)}
              >
                {showAdvancedAc ? "Hide Advanced" : "Advanced"}
              </Button>
            </div>

            {renderRulesTable(filteredMacroRules, { showMacro: true })}

            <section className="space-y-2 rounded-md border p-3">
              <h2 className="text-sm font-semibold">Preview sample name</h2>
              <div className="flex flex-wrap gap-2">
                <Select value={previewClass} onValueChange={setPreviewClass}>
                  <SelectTrigger className="w-[220px]">
                    <SelectValue placeholder="Asset class" />
                  </SelectTrigger>
                  <SelectContent>
                    {macroGroupClasses.map((c) => (
                      <SelectItem key={c.asset_class} value={c.asset_class}>
                        {formatClassLabel(c.asset_class)}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <Input
                  className="min-w-[280px] flex-1"
                  placeholder="e.g. VANGUARD S&P 500 ETF"
                  value={previewName}
                  onChange={(e) => setPreviewName(e.target.value)}
                />
                <Button variant="secondary" onClick={() => void onPreview()}>
                  Preview
                </Button>
              </div>
              {previewResult ? (
                <p className="text-sm">
                  Result: <span className="font-medium">{previewResult}</span>
                </p>
              ) : null}
            </section>
          </div>
        ) : !selected ? (
          <p className="text-sm text-muted-foreground">Select a Macro or asset class.</p>
        ) : (
          <>
            <div className="space-y-3">
              <div className="flex items-center justify-between gap-3">
                <div>
                  <p className="text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
                    {macroForAssetClass(selected.asset_class)}
                  </p>
                  <h1 className="text-xl font-semibold">
                    {formatClassLabel(selected.asset_class)}
                  </h1>
                </div>
                <div className="flex items-center gap-2">
                  <Label htmlFor="class-enabled" className="text-sm">
                    Enabled
                  </Label>
                  <Switch
                    id="class-enabled"
                    checked={selected.enabled}
                    onCheckedChange={(v) => void onToggleEnabled(v)}
                  />
                </div>
              </div>
              <p className="text-sm text-muted-foreground">
                Ordered name rules assign an Asset Type. If none match, the Macro
                Common AT is used (saved to every class under{" "}
                {macroForAssetClass(selected.asset_class)}).
              </p>
              <div className="flex flex-wrap items-end gap-2">
                <div className="min-w-[240px] flex-1 space-y-1">
                  <Label htmlFor="default-at">Common Asset Type (Macro)</Label>
                  <Input
                    id="default-at"
                    value={defaultAt}
                    onChange={(e) => setDefaultAt(e.target.value)}
                    disabled={savingClass}
                  />
                </div>
                <Button onClick={() => void onSaveDefault()} disabled={savingClass}>
                  {savingClass ? "Saving…" : "Save Common AT"}
                </Button>
              </div>
            </div>

            <section className="space-y-3">
              <h2 className="text-sm font-semibold">Rules (first match wins)</h2>
              {renderAddRuleForm("class")}
              {renderRulesTable(rules, { showMacro: false })}
              <p className="text-xs text-muted-foreground">
                else → {defaultAt || selected.default_asset_type || "Common AT"}
              </p>
            </section>

            <section className="space-y-2 rounded-md border p-3">
              <h2 className="text-sm font-semibold">Preview sample name</h2>
              <div className="flex flex-wrap gap-2">
                <Input
                  className="min-w-[280px] flex-1"
                  placeholder="e.g. VANGUARD S&P 500 ETF"
                  value={previewName}
                  onChange={(e) => setPreviewName(e.target.value)}
                />
                <Button variant="secondary" onClick={() => void onPreview()}>
                  Preview
                </Button>
              </div>
              {previewResult ? (
                <p className="text-sm">
                  Result: <span className="font-medium">{previewResult}</span>
                </p>
              ) : null}
            </section>
          </>
        )}
      </main>
    </div>
  )
}
