import { defineStore } from "pinia"
import { computed, ref } from "vue"

/**
 * State for a single DependencyListBuilder instance. Multiple builders
 * can coexist on a page; each is keyed by `builderId`.
 *
 * A "dependency list" is an ordered set of conditional rules — each
 * with a name, conditions (combined via AND / OR), and actions to run
 * when the conditions match.
 */

export type DependencyOperator =
  | "equals"
  | "not_equals"
  | "greater_than"
  | "less_than"
  | "contains"
  | "is_truthy"
  | "is_falsy"

export type DependencyActionType =
  | "show"
  | "hide"
  | "require"
  | "set_value"
  | "emit_event"

export interface DependencyCondition {
  field: string
  operator: DependencyOperator
  value?: unknown
}

export interface DependencyAction {
  type: DependencyActionType
  target?: string
  payload?: unknown
}

export interface DependencyRule {
  id: string
  name: string
  enabled: boolean
  conditions: DependencyCondition[]
  conditionsLogic: "AND" | "OR"
  actions: DependencyAction[]
}

export interface DependencyListBuilderSchema {
  rules: DependencyRule[]
}

/** Identifies which rule is currently selected for editing. */
export interface DependencyListBuilderSelection {
  id: string | null
}

let idCounter = 0
function nextId(prefix: string): string {
  idCounter += 1
  return `${prefix}-${Date.now()}-${idCounter}`
}

/** Test helper to make ids deterministic. */
export function __resetDependencyListBuilderIdCounter(): void {
  idCounter = 0
}

function emptySchema(): DependencyListBuilderSchema {
  return { rules: [] }
}

function defaultCondition(): DependencyCondition {
  return { field: "", operator: "equals", value: "" }
}

function defaultAction(): DependencyAction {
  return { type: "show", target: "", payload: undefined }
}

/** Built-in rule templates exposed to the palette. */
export type DependencyRuleTemplate =
  | "blank"
  | "show-field"
  | "hide-field"
  | "set-value"

export function buildRuleFromTemplate(
  template: DependencyRuleTemplate,
  id: string,
): DependencyRule {
  switch (template) {
    case "show-field":
      return {
        id,
        name: "Show field",
        enabled: true,
        conditions: [defaultCondition()],
        conditionsLogic: "AND",
        actions: [{ type: "show", target: "" }],
      }
    case "hide-field":
      return {
        id,
        name: "Hide field",
        enabled: true,
        conditions: [defaultCondition()],
        conditionsLogic: "AND",
        actions: [{ type: "hide", target: "" }],
      }
    case "set-value":
      return {
        id,
        name: "Set value",
        enabled: true,
        conditions: [defaultCondition()],
        conditionsLogic: "AND",
        actions: [{ type: "set_value", target: "", payload: "" }],
      }
    case "blank":
    default:
      return {
        id,
        name: "New rule",
        enabled: true,
        conditions: [],
        conditionsLogic: "AND",
        actions: [],
      }
  }
}

export const useDependencyListBuilderStore = defineStore(
  "gx-modui/dependencyListBuilder",
  () => {
    const builders = ref<Record<string, DependencyListBuilderSchema>>({})
    const selections = ref<Record<string, DependencyListBuilderSelection>>({})

    function ensureBuilder(builderId: string): DependencyListBuilderSchema {
      if (!builders.value[builderId]) {
        builders.value[builderId] = emptySchema()
        selections.value[builderId] = { id: null }
      }
      return builders.value[builderId]!
    }

    function loadSchema(
      builderId: string,
      schema: DependencyListBuilderSchema,
    ): void {
      builders.value[builderId] = {
        rules: schema.rules.map((rule) => ({
          ...rule,
          conditions: rule.conditions.map((c) => ({ ...c })),
          actions: rule.actions.map((a) => ({ ...a })),
        })),
      }
      selections.value[builderId] = { id: null }
    }

    function resetBuilder(builderId: string): void {
      delete builders.value[builderId]
      delete selections.value[builderId]
    }

    /** Append a new rule built from `template` and return its id. */
    function addRule(
      builderId: string,
      template: DependencyRuleTemplate = "blank",
    ): string {
      const schema = ensureBuilder(builderId)
      const id = nextId("rule")
      const rule = buildRuleFromTemplate(template, id)
      schema.rules = [...schema.rules, rule]
      return id
    }

    function removeRule(builderId: string, ruleId: string): void {
      const schema = ensureBuilder(builderId)
      schema.rules = schema.rules.filter((r) => r.id !== ruleId)
      const sel = selections.value[builderId]
      if (sel?.id === ruleId) {
        selections.value[builderId] = { id: null }
      }
    }

    function reorderRule(
      builderId: string,
      fromIndex: number,
      toIndex: number,
    ): void {
      const schema = ensureBuilder(builderId)
      const list = schema.rules
      if (
        fromIndex < 0 ||
        fromIndex >= list.length ||
        toIndex < 0 ||
        toIndex >= list.length ||
        fromIndex === toIndex
      ) {
        return
      }
      const next = [...list]
      const [moved] = next.splice(fromIndex, 1)
      if (moved !== undefined) {
        next.splice(toIndex, 0, moved)
        schema.rules = next
      }
    }

    function updateRule(
      builderId: string,
      ruleId: string,
      patch: Partial<Omit<DependencyRule, "id">>,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      const list = [...schema.rules]
      list[idx] = { ...current, ...patch }
      schema.rules = list
    }

    function addCondition(builderId: string, ruleId: string): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      const list = [...schema.rules]
      list[idx] = {
        ...current,
        conditions: [...current.conditions, defaultCondition()],
      }
      schema.rules = list
    }

    function updateCondition(
      builderId: string,
      ruleId: string,
      conditionIndex: number,
      patch: Partial<DependencyCondition>,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      if (conditionIndex < 0 || conditionIndex >= current.conditions.length) {
        return
      }
      const conditions = [...current.conditions]
      conditions[conditionIndex] = {
        ...conditions[conditionIndex]!,
        ...patch,
      }
      const list = [...schema.rules]
      list[idx] = { ...current, conditions }
      schema.rules = list
    }

    function removeCondition(
      builderId: string,
      ruleId: string,
      conditionIndex: number,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      if (conditionIndex < 0 || conditionIndex >= current.conditions.length) {
        return
      }
      const conditions = current.conditions.filter(
        (_, i) => i !== conditionIndex,
      )
      const list = [...schema.rules]
      list[idx] = { ...current, conditions }
      schema.rules = list
    }

    function addAction(builderId: string, ruleId: string): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      const list = [...schema.rules]
      list[idx] = {
        ...current,
        actions: [...current.actions, defaultAction()],
      }
      schema.rules = list
    }

    function updateAction(
      builderId: string,
      ruleId: string,
      actionIndex: number,
      patch: Partial<DependencyAction>,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      if (actionIndex < 0 || actionIndex >= current.actions.length) {
        return
      }
      const actions = [...current.actions]
      actions[actionIndex] = {
        ...actions[actionIndex]!,
        ...patch,
      }
      const list = [...schema.rules]
      list[idx] = { ...current, actions }
      schema.rules = list
    }

    function removeAction(
      builderId: string,
      ruleId: string,
      actionIndex: number,
    ): void {
      const schema = ensureBuilder(builderId)
      const idx = schema.rules.findIndex((r) => r.id === ruleId)
      if (idx < 0) {
        return
      }
      const current = schema.rules[idx]!
      if (actionIndex < 0 || actionIndex >= current.actions.length) {
        return
      }
      const actions = current.actions.filter((_, i) => i !== actionIndex)
      const list = [...schema.rules]
      list[idx] = { ...current, actions }
      schema.rules = list
    }

    function select(builderId: string, id: string | null): void {
      selections.value[builderId] = { id }
    }

    function getSelection(
      builderId: string,
    ): DependencyListBuilderSelection {
      return selections.value[builderId] ?? { id: null }
    }

    function getSchema(builderId: string): DependencyListBuilderSchema {
      return ensureBuilder(builderId)
    }

    return {
      builders,
      selections,
      ensureBuilder,
      loadSchema,
      resetBuilder,
      addRule,
      removeRule,
      reorderRule,
      updateRule,
      addCondition,
      updateCondition,
      removeCondition,
      addAction,
      updateAction,
      removeAction,
      select,
      getSelection,
      getSchema,
    }
  },
)

/** Composable wrapper around `useDependencyListBuilderStore`. */
export function useDependencyListBuilder(builderId: string) {
  const store = useDependencyListBuilderStore()
  store.ensureBuilder(builderId)
  return {
    schema: computed<DependencyListBuilderSchema>(() =>
      store.getSchema(builderId),
    ),
    selection: computed<DependencyListBuilderSelection>(() =>
      store.getSelection(builderId),
    ),
    addRule: (template?: DependencyRuleTemplate) =>
      store.addRule(builderId, template),
    removeRule: (id: string) => store.removeRule(builderId, id),
    reorderRule: (from: number, to: number) =>
      store.reorderRule(builderId, from, to),
    updateRule: (id: string, patch: Partial<Omit<DependencyRule, "id">>) =>
      store.updateRule(builderId, id, patch),
    addCondition: (id: string) => store.addCondition(builderId, id),
    updateCondition: (
      id: string,
      index: number,
      patch: Partial<DependencyCondition>,
    ) => store.updateCondition(builderId, id, index, patch),
    removeCondition: (id: string, index: number) =>
      store.removeCondition(builderId, id, index),
    addAction: (id: string) => store.addAction(builderId, id),
    updateAction: (
      id: string,
      index: number,
      patch: Partial<DependencyAction>,
    ) => store.updateAction(builderId, id, index, patch),
    removeAction: (id: string, index: number) =>
      store.removeAction(builderId, id, index),
    select: (id: string | null) => store.select(builderId, id),
    loadSchema: (schema: DependencyListBuilderSchema) =>
      store.loadSchema(builderId, schema),
    reset: () => store.resetBuilder(builderId),
  }
}
