import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

import {
  DEFAULT_USER_SETTINGS,
  mergeSettings,
  type PbiIntent,
  type PendingSprintDraft,
  type UserSettings,
} from '@/lib/user-settings'
import { updateUserSettingsAction } from '@/actions/user-settings'

type SettingsPath = readonly (string | number)[]

interface PendingMutation {
  id: number
  prev: UserSettings
}

interface UserSettingsState {
  entities: { settings: UserSettings }
  context: {
    hydrated: boolean
    isDemo: boolean
  }
  pendingMutations: Record<number, PendingMutation>
}

interface UserSettingsActions {
  hydrate: (initial: UserSettings, isDemo: boolean) => void
  setPref: (path: SettingsPath, value: unknown) => Promise<void>
  applyServerPatch: (patch: Partial<UserSettings>) => void
  setPendingSprintDraft: (
    productId: string,
    draft: PendingSprintDraft,
  ) => Promise<void>
  clearPendingSprintDraft: (productId: string) => Promise<void>
  upsertPbiIntent: (
    productId: string,
    pbiId: string,
    intent: PbiIntent,
  ) => Promise<void>
  upsertStoryOverride: (
    productId: string,
    pbiId: string,
    storyId: string,
    kind: 'add' | 'remove' | 'clear',
  ) => Promise<void>
}

let nextMutationId = 1

function patchFromPath(path: SettingsPath, value: unknown): Partial<UserSettings> {
  if (path.length === 0) {
    if (value && typeof value === 'object' && !Array.isArray(value)) {
      return value as Partial<UserSettings>
    }
    return {}
  }
  const out: Record<string, unknown> = {}
  let cursor: Record<string, unknown> = out
  for (let i = 0; i < path.length - 1; i++) {
    const key = String(path[i])
    cursor[key] = {}
    cursor = cursor[key] as Record<string, unknown>
  }
  cursor[String(path[path.length - 1])] = value
  return out as Partial<UserSettings>
}

export const useUserSettingsStore = create<UserSettingsState & UserSettingsActions>()(
  immer((set, get) => ({
    entities: { settings: DEFAULT_USER_SETTINGS },
    context: { hydrated: false, isDemo: false },
    pendingMutations: {},

    hydrate: (initial, isDemo) => {
      set((draft) => {
        // PBI-79 scope-aanpassing: pendingSprintDraft is session-only;
        // eventuele legacy DB-entries van vóór deze aanpassing worden bij
        // hydratatie weggegooid zodat de draft niet 'spookt'.
        const stripped: UserSettings = { ...initial }
        if (stripped.workflow?.pendingSprintDraft) {
          stripped.workflow = { ...stripped.workflow }
          delete stripped.workflow.pendingSprintDraft
        }
        draft.entities.settings = stripped
        draft.context.hydrated = true
        draft.context.isDemo = isDemo
      })
    },

    applyServerPatch: (patch) => {
      set((draft) => {
        draft.entities.settings = mergeSettings(
          draft.entities.settings as UserSettings,
          patch,
        ) as UserSettings
      })
    },

    setPendingSprintDraft: async (productId, draft) => {
      // PBI-79 scope-aanpassing: session-only. Geen server-roundtrip;
      // de draft leeft uitsluitend in deze store-instantie en is bij
      // page-refresh/leave weg (zie SprintDraftLeaveGuard voor de
      // beforeunload-warning).
      set((s) => {
        if (!s.entities.settings.workflow) s.entities.settings.workflow = {}
        if (!s.entities.settings.workflow.pendingSprintDraft) {
          s.entities.settings.workflow.pendingSprintDraft = {}
        }
        s.entities.settings.workflow.pendingSprintDraft[productId] = draft
      })
    },

    clearPendingSprintDraft: async (productId) => {
      // PBI-79 scope-aanpassing: session-only — lokale delete is voldoende.
      set((s) => {
        const map = s.entities.settings.workflow?.pendingSprintDraft
        if (map) delete map[productId]
      })
    },

    upsertPbiIntent: async (productId, pbiId, intent) => {
      const current =
        get().entities.settings.workflow?.pendingSprintDraft?.[productId]
      if (!current) return
      const nextOverrides = { ...current.storyOverrides }
      delete nextOverrides[pbiId]
      const next: PendingSprintDraft = {
        ...current,
        pbiIntent: { ...current.pbiIntent, [pbiId]: intent },
        storyOverrides: nextOverrides,
      }
      await get().setPendingSprintDraft(productId, next)
    },

    upsertStoryOverride: async (productId, pbiId, storyId, kind) => {
      const current =
        get().entities.settings.workflow?.pendingSprintDraft?.[productId]
      if (!current) return
      const existing = current.storyOverrides[pbiId] ?? { add: [], remove: [] }
      const dropFrom = (arr: string[]) => arr.filter((id) => id !== storyId)
      let nextEntry: { add: string[]; remove: string[] }
      switch (kind) {
        case 'add':
          nextEntry = {
            add: existing.add.includes(storyId) ? existing.add : [...existing.add, storyId],
            remove: dropFrom(existing.remove),
          }
          break
        case 'remove':
          nextEntry = {
            add: dropFrom(existing.add),
            remove: existing.remove.includes(storyId)
              ? existing.remove
              : [...existing.remove, storyId],
          }
          break
        case 'clear':
        default:
          nextEntry = { add: dropFrom(existing.add), remove: dropFrom(existing.remove) }
          break
      }
      const nextOverrides = { ...current.storyOverrides }
      if (nextEntry.add.length === 0 && nextEntry.remove.length === 0) {
        delete nextOverrides[pbiId]
      } else {
        nextOverrides[pbiId] = nextEntry
      }
      const next: PendingSprintDraft = { ...current, storyOverrides: nextOverrides }
      await get().setPendingSprintDraft(productId, next)
    },

    setPref: async (path, value) => {
      const patch = patchFromPath(path, value)

      // Demo: lokale merge zonder server-call.
      if (get().context.isDemo) {
        set((draft) => {
          draft.entities.settings = mergeSettings(
            draft.entities.settings as UserSettings,
            patch,
          ) as UserSettings
        })
        return
      }

      const mutationId = nextMutationId++
      const prev = get().entities.settings as UserSettings

      // Optimistic.
      set((draft) => {
        draft.entities.settings = mergeSettings(
          draft.entities.settings as UserSettings,
          patch,
        ) as UserSettings
        draft.pendingMutations[mutationId] = { id: mutationId, prev }
      })

      const result = await updateUserSettingsAction(patch)

      set((draft) => {
        delete draft.pendingMutations[mutationId]
        if ('error' in result) {
          // Rollback alleen als geen latere mutatie de waarde alweer heeft overschreven.
          draft.entities.settings = prev as UserSettings
        } else {
          // Settle: server-merge is autoritatief.
          draft.entities.settings = result.settings as UserSettings
        }
      })
    },
  })),
)
