<script setup lang="ts">
/**
 * DependencyListBuilder — visual editor for a list of conditional
 * rules of the shape "when X happens then Y".
 *
 * Three-pane layout (matches FormBuilder):
 *   - LEFT (Palette):    "Add rule" + a handful of rule templates.
 *   - MIDDLE (Canvas):   ordered list of rules; click a row to select.
 *   - RIGHT (Inspector): edits the selected rule — name, enabled,
 *                        conditions (add/edit/remove), actions
 *                        (add/edit/remove), conditionsLogic.
 *
 * State is owned by `dependencyListBuilderStore`. Two-way binds the
 * full `DependencyListBuilderSchema` via `defineModel`.
 */
import { computed, onMounted, ref, watch } from "vue"
import { Plus, Trash2, X } from "@lucide/vue"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
  Card,
  CardContent,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import {
  FieldFrame,
  type FieldFrameProps,
} from "@/components/ui/field-frame"
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 {
  useDependencyListBuilder,
  type DependencyAction,
  type DependencyActionType,
  type DependencyCondition,
  type DependencyListBuilderSchema,
  type DependencyOperator,
  type DependencyRule,
  type DependencyRuleTemplate,
} from "@/stores/dependencyListBuilderStore"
import { cn } from "@/lib/utils"

export interface DependencyListBuilderProps
  extends Pick<FieldFrameProps, "label" | "hideLabel" | "helpText" | "errors"> {
  /** Initial / current schema. Two-way bindable via v-model. */
  modelValue?: DependencyListBuilderSchema
  /**
   * Identifier used to key the dependencyListBuilderStore. Defaults to
   * a stable auto-generated id so multiple builders on the same page
   * don't collide.
   */
  builderId?: string
  name?: string | null
  item?: Record<string, unknown>
  classList?: string[] | string
}

const props = withDefaults(defineProps<DependencyListBuilderProps>(), {
  errors: () => [],
  item: () => ({}),
})

const model = defineModel<DependencyListBuilderSchema | undefined>(
  "modelValue",
)

let nextAutoId = 0
function makeAutoId(): string {
  nextAutoId += 1
  return `dep-list-builder-${Date.now()}-${nextAutoId}`
}
const fallbackId = ref<string>(makeAutoId())
const builderId = computed<string>(() => props.builderId ?? fallbackId.value)

const builder = useDependencyListBuilder(builderId.value)

function isSchema(value: unknown): value is DependencyListBuilderSchema {
  if (typeof value !== "object" || value === null) {
    return false
  }
  return Array.isArray((value as Record<string, unknown>).rules)
}

onMounted(() => {
  const seed = model.value
  if (isSchema(seed)) {
    builder.loadSchema(seed)
  }
})

watch(
  () => builder.schema.value,
  (next) => {
    model.value = next
  },
  { deep: true },
)

const rules = computed<DependencyRule[]>(() => builder.schema.value.rules)
const selectedId = computed<string | null>(() => builder.selection.value.id)
const selectedRule = computed<DependencyRule | null>(() => {
  const id = selectedId.value
  if (!id) {
    return null
  }
  return rules.value.find((r) => r.id === id) ?? null
})

const OPERATORS: { label: string; value: DependencyOperator }[] = [
  { label: "equals", value: "equals" },
  { label: "not equals", value: "not_equals" },
  { label: "greater than", value: "greater_than" },
  { label: "less than", value: "less_than" },
  { label: "contains", value: "contains" },
  { label: "is truthy", value: "is_truthy" },
  { label: "is falsy", value: "is_falsy" },
]

const ACTION_TYPES: { label: string; value: DependencyActionType }[] = [
  { label: "Show field", value: "show" },
  { label: "Hide field", value: "hide" },
  { label: "Require field", value: "require" },
  { label: "Set value", value: "set_value" },
  { label: "Emit event", value: "emit_event" },
]

const LOGIC: { label: string; value: "AND" | "OR" }[] = [
  { label: "AND (all)", value: "AND" },
  { label: "OR (any)", value: "OR" },
]

const PALETTE: { label: string; template: DependencyRuleTemplate }[] = [
  { label: "Add rule", template: "blank" },
  { label: "Show field", template: "show-field" },
  { label: "Hide field", template: "hide-field" },
  { label: "Set value", template: "set-value" },
]

function onPaletteAdd(template: DependencyRuleTemplate): void {
  const id = builder.addRule(template)
  builder.select(id)
}

function onRuleClick(ruleId: string): void {
  builder.select(ruleId)
}

function onClearSelection(): void {
  builder.select(null)
}

function onRemoveSelected(): void {
  const id = selectedId.value
  if (id) {
    builder.removeRule(id)
  }
}

function patchSelected(patch: Partial<Omit<DependencyRule, "id">>): void {
  const id = selectedId.value
  if (id) {
    builder.updateRule(id, patch)
  }
}

function onNameInput(e: Event): void {
  patchSelected({ name: (e.target as HTMLInputElement).value })
}

function onEnabledChange(v: boolean): void {
  patchSelected({ enabled: v })
}

function onLogicChange(v: unknown): void {
  if (v === "AND" || v === "OR") {
    patchSelected({ conditionsLogic: v })
  }
}

function onAddCondition(): void {
  const id = selectedId.value
  if (id) {
    builder.addCondition(id)
  }
}

function onConditionFieldInput(index: number, e: Event): void {
  const id = selectedId.value
  if (id) {
    builder.updateCondition(id, index, {
      field: (e.target as HTMLInputElement).value,
    })
  }
}

function onConditionOperatorChange(index: number, v: unknown): void {
  const id = selectedId.value
  if (id && isOperator(v)) {
    builder.updateCondition(id, index, { operator: v })
  }
}

function onConditionValueInput(index: number, e: Event): void {
  const id = selectedId.value
  if (id) {
    builder.updateCondition(id, index, {
      value: (e.target as HTMLInputElement).value,
    })
  }
}

function onRemoveCondition(index: number): void {
  const id = selectedId.value
  if (id) {
    builder.removeCondition(id, index)
  }
}

function onAddAction(): void {
  const id = selectedId.value
  if (id) {
    builder.addAction(id)
  }
}

function onActionTypeChange(index: number, v: unknown): void {
  const id = selectedId.value
  if (id && isActionType(v)) {
    builder.updateAction(id, index, { type: v })
  }
}

function onActionTargetInput(index: number, e: Event): void {
  const id = selectedId.value
  if (id) {
    builder.updateAction(id, index, {
      target: (e.target as HTMLInputElement).value,
    })
  }
}

function onActionPayloadInput(index: number, e: Event): void {
  const id = selectedId.value
  if (id) {
    builder.updateAction(id, index, {
      payload: (e.target as HTMLInputElement).value,
    })
  }
}

function onRemoveAction(index: number): void {
  const id = selectedId.value
  if (id) {
    builder.removeAction(id, index)
  }
}

function isOperator(value: unknown): value is DependencyOperator {
  return OPERATORS.some((o) => o.value === value)
}

function isActionType(value: unknown): value is DependencyActionType {
  return ACTION_TYPES.some((a) => a.value === value)
}

function conditionValueString(c: DependencyCondition): string {
  if (c.value === null || c.value === undefined) {
    return ""
  }
  return String(c.value)
}

function actionTargetString(a: DependencyAction): string {
  return a.target ?? ""
}

function actionPayloadString(a: DependencyAction): string {
  if (a.payload === null || a.payload === undefined) {
    return ""
  }
  return String(a.payload)
}
</script>

<template>
  <FieldFrame
    :label="props.label"
    :hide-label="props.hideLabel"
    :help-text="props.helpText"
    :errors="props.errors"
    :class="props.classList"
  >
    <div
      :class="cn('gxp-dependency-list-builder grid gap-3 rounded-lg border bg-card')"
      style="grid-template-columns: 220px 1fr 320px; min-height: 24rem"
      data-testid="dependency-list-builder"
      :data-element-name="props.name ?? undefined"
    >
      <aside
        class="flex flex-col gap-2 border-r p-3 overflow-y-auto"
        data-testid="dependency-list-builder-palette"
      >
        <h4 class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
          Rule palette
        </h4>
        <div class="flex flex-col gap-1">
          <button
            v-for="entry in PALETTE"
            :key="entry.template"
            type="button"
            class="flex items-start gap-2 rounded-md border bg-background px-2 py-1.5 text-left text-xs transition-colors hover:border-primary hover:bg-accent/40"
            :data-testid="`dependency-list-builder-palette-${entry.template}`"
            @click="onPaletteAdd(entry.template)"
          >
            <Plus class="mt-0.5 h-3.5 w-3.5 shrink-0" />
            <span class="font-medium">{{ entry.label }}</span>
          </button>
        </div>
      </aside>

      <section
        class="flex flex-col gap-3 overflow-y-auto p-3"
        data-testid="dependency-list-builder-canvas"
      >
        <header class="flex items-center justify-between">
          <h4 class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
            Rules
          </h4>
          <Badge variant="outline" class="font-mono text-[10px]">
            builder/dependency-list-builder
          </Badge>
        </header>

        <p
          v-if="rules.length === 0"
          class="py-6 text-center text-xs text-muted-foreground"
          data-testid="dependency-list-builder-canvas-empty"
        >
          Click "Add rule" in the palette to create a rule.
        </p>

        <ul
          v-else
          class="flex flex-col gap-1 text-xs"
          data-testid="dependency-list-builder-canvas-list"
        >
          <li
            v-for="(rule, index) in rules"
            :key="rule.id"
            class="flex cursor-pointer items-center gap-2 rounded border bg-background px-2 py-1.5 transition-colors"
            :class="
              selectedId === rule.id ? 'border-primary bg-accent/30' : ''
            "
            :data-testid="`dependency-list-builder-row-${rule.id}`"
            @click="onRuleClick(rule.id)"
          >
            <span class="w-6 text-right tabular-nums text-muted-foreground">
              {{ index + 1 }}.
            </span>
            <span class="font-medium">{{ rule.name || "(unnamed)" }}</span>
            <Badge variant="secondary" class="font-mono text-[9px]">
              {{ rule.conditions.length }}c / {{ rule.actions.length }}a
            </Badge>
            <Switch
              class="ml-auto"
              :model-value="rule.enabled"
              :data-testid="`dependency-list-builder-row-enabled-${rule.id}`"
              @click.stop
              @update:model-value="
                (v: boolean) => builder.updateRule(rule.id, { enabled: v })
              "
            />
          </li>
        </ul>
      </section>

      <aside
        class="flex flex-col gap-2 overflow-y-auto border-l p-3"
        data-testid="dependency-list-builder-inspector"
      >
        <header class="flex items-center justify-between">
          <h4 class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
            Inspector
          </h4>
          <Button
            v-if="selectedId"
            variant="ghost"
            size="icon-sm"
            aria-label="Clear selection"
            @click="onClearSelection"
          >
            <X class="h-3.5 w-3.5" />
          </Button>
        </header>

        <p
          v-if="!selectedRule"
          class="py-3 text-xs text-muted-foreground"
          data-testid="dependency-list-builder-inspector-empty"
        >
          Select a rule on the canvas to edit it.
        </p>

        <div
          v-else
          class="flex flex-col gap-3"
          data-testid="dependency-list-builder-inspector-fields"
        >
          <Card class="border-dashed">
            <CardHeader class="py-2">
              <CardTitle class="font-mono text-xs uppercase tracking-wide">
                Rule
              </CardTitle>
            </CardHeader>
            <CardContent class="flex flex-col gap-2 pb-3">
              <div class="flex flex-col gap-1">
                <Label for="dep-name">Name</Label>
                <Input
                  id="dep-name"
                  :model-value="selectedRule.name"
                  data-testid="dependency-list-builder-inspector-name"
                  @input="onNameInput"
                />
              </div>

              <div class="flex items-center justify-between rounded border bg-background px-2 py-1">
                <Label for="dep-enabled">Enabled</Label>
                <Switch
                  id="dep-enabled"
                  :model-value="selectedRule.enabled"
                  data-testid="dependency-list-builder-inspector-enabled"
                  @update:model-value="onEnabledChange"
                />
              </div>

              <div class="flex flex-col gap-1">
                <Label>Conditions logic</Label>
                <Select
                  :model-value="selectedRule.conditionsLogic"
                  @update:model-value="onLogicChange"
                >
                  <SelectTrigger
                    data-testid="dependency-list-builder-inspector-logic"
                  >
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectItem
                      v-for="opt in LOGIC"
                      :key="opt.value"
                      :value="opt.value"
                    >
                      {{ opt.label }}
                    </SelectItem>
                  </SelectContent>
                </Select>
              </div>
            </CardContent>
          </Card>

          <Card class="border-dashed">
            <CardHeader class="flex flex-row items-center justify-between py-2">
              <CardTitle class="font-mono text-xs uppercase tracking-wide">
                Conditions ({{ selectedRule.conditions.length }})
              </CardTitle>
              <Button
                variant="outline"
                size="sm"
                data-testid="dependency-list-builder-add-condition"
                @click="onAddCondition"
              >
                <Plus class="h-3.5 w-3.5" />
                Add
              </Button>
            </CardHeader>
            <CardContent class="flex flex-col gap-3 pb-3">
              <p
                v-if="selectedRule.conditions.length === 0"
                class="text-xs text-muted-foreground"
              >
                No conditions yet.
              </p>
              <div
                v-for="(condition, index) in selectedRule.conditions"
                :key="index"
                class="flex flex-col gap-1 rounded border bg-background p-2"
                :data-testid="`dependency-list-builder-condition-${index}`"
              >
                <div class="flex items-center justify-between">
                  <span class="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
                    #{{ index + 1 }}
                  </span>
                  <Button
                    variant="ghost"
                    size="icon-sm"
                    aria-label="Remove condition"
                    :data-testid="`dependency-list-builder-condition-remove-${index}`"
                    @click="onRemoveCondition(index)"
                  >
                    <Trash2 class="h-3.5 w-3.5" />
                  </Button>
                </div>
                <div class="flex flex-col gap-1">
                  <Label :for="`dep-cond-field-${index}`">Field</Label>
                  <Input
                    :id="`dep-cond-field-${index}`"
                    :model-value="condition.field"
                    :data-testid="`dependency-list-builder-condition-field-${index}`"
                    @input="(e: Event) => onConditionFieldInput(index, e)"
                  />
                </div>
                <div class="flex flex-col gap-1">
                  <Label>Operator</Label>
                  <Select
                    :model-value="condition.operator"
                    @update:model-value="(v: unknown) => onConditionOperatorChange(index, v)"
                  >
                    <SelectTrigger
                      :data-testid="`dependency-list-builder-condition-operator-${index}`"
                    >
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem
                        v-for="op in OPERATORS"
                        :key="op.value"
                        :value="op.value"
                      >
                        {{ op.label }}
                      </SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div
                  v-if="condition.operator !== 'is_truthy' && condition.operator !== 'is_falsy'"
                  class="flex flex-col gap-1"
                >
                  <Label :for="`dep-cond-value-${index}`">Value</Label>
                  <Input
                    :id="`dep-cond-value-${index}`"
                    :model-value="conditionValueString(condition)"
                    :data-testid="`dependency-list-builder-condition-value-${index}`"
                    @input="(e: Event) => onConditionValueInput(index, e)"
                  />
                </div>
              </div>
            </CardContent>
          </Card>

          <Card class="border-dashed">
            <CardHeader class="flex flex-row items-center justify-between py-2">
              <CardTitle class="font-mono text-xs uppercase tracking-wide">
                Actions ({{ selectedRule.actions.length }})
              </CardTitle>
              <Button
                variant="outline"
                size="sm"
                data-testid="dependency-list-builder-add-action"
                @click="onAddAction"
              >
                <Plus class="h-3.5 w-3.5" />
                Add
              </Button>
            </CardHeader>
            <CardContent class="flex flex-col gap-3 pb-3">
              <p
                v-if="selectedRule.actions.length === 0"
                class="text-xs text-muted-foreground"
              >
                No actions yet.
              </p>
              <div
                v-for="(action, index) in selectedRule.actions"
                :key="index"
                class="flex flex-col gap-1 rounded border bg-background p-2"
                :data-testid="`dependency-list-builder-action-${index}`"
              >
                <div class="flex items-center justify-between">
                  <span class="text-[10px] font-semibold uppercase tracking-wide text-muted-foreground">
                    #{{ index + 1 }}
                  </span>
                  <Button
                    variant="ghost"
                    size="icon-sm"
                    aria-label="Remove action"
                    :data-testid="`dependency-list-builder-action-remove-${index}`"
                    @click="onRemoveAction(index)"
                  >
                    <Trash2 class="h-3.5 w-3.5" />
                  </Button>
                </div>
                <div class="flex flex-col gap-1">
                  <Label>Type</Label>
                  <Select
                    :model-value="action.type"
                    @update:model-value="(v: unknown) => onActionTypeChange(index, v)"
                  >
                    <SelectTrigger
                      :data-testid="`dependency-list-builder-action-type-${index}`"
                    >
                      <SelectValue />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem
                        v-for="t in ACTION_TYPES"
                        :key="t.value"
                        :value="t.value"
                      >
                        {{ t.label }}
                      </SelectItem>
                    </SelectContent>
                  </Select>
                </div>
                <div class="flex flex-col gap-1">
                  <Label :for="`dep-action-target-${index}`">Target</Label>
                  <Input
                    :id="`dep-action-target-${index}`"
                    :model-value="actionTargetString(action)"
                    :data-testid="`dependency-list-builder-action-target-${index}`"
                    @input="(e: Event) => onActionTargetInput(index, e)"
                  />
                </div>
                <div class="flex flex-col gap-1">
                  <Label :for="`dep-action-payload-${index}`">Payload</Label>
                  <Input
                    :id="`dep-action-payload-${index}`"
                    :model-value="actionPayloadString(action)"
                    :data-testid="`dependency-list-builder-action-payload-${index}`"
                    @input="(e: Event) => onActionPayloadInput(index, e)"
                  />
                </div>
              </div>
            </CardContent>
          </Card>

          <div class="flex items-center gap-1">
            <Button
              variant="destructive"
              size="sm"
              class="ml-auto"
              data-testid="dependency-list-builder-remove"
              @click="onRemoveSelected"
            >
              <Trash2 class="h-3.5 w-3.5" />
              Remove rule
            </Button>
          </div>
        </div>
      </aside>
    </div>
  </FieldFrame>
</template>
