import { create } from 'zustand'
import { v4 as uuidv4 } from 'uuid'
import { supabaseProcessRepo, type ProcessWithUserNames } from '@/persistence/supabase/process-repo'
import { supabaseGroupRepo } from '@/persistence/supabase/group-repo'
import { supabaseSettingsRepo } from '@/persistence/supabase/settings-repo'
import { supabaseSimulationRepo } from '@/persistence/supabase/simulation-repo'
import type { ProcessGroup, AppSettings, Simulation } from '@/domain/types'

interface LibraryState {
  groups: ProcessGroup[]
  processes: ProcessWithUserNames[]
  settings: AppSettings
  isLoading: boolean
  error: string | null

  load: () => Promise<void>

  createGroup: (name: string, userId: string) => Promise<ProcessGroup>
  deleteGroup: (id: string) => Promise<void>

  deleteProcess: (id: string) => Promise<void>

  assignProcessToGroup: (processId: string, groupId: string | null, userId: string) => Promise<void>

  getProcessesByGroup: (groupId: string | null) => ProcessWithUserNames[]
  getLatestSimulation: (processId: string) => Promise<Simulation | undefined>

  updateSettings: (updates: Partial<Omit<AppSettings, 'id'>>) => Promise<void>
  getAllTags: () => string[]
}

const DEFAULT_SETTINGS: AppSettings = { id: 'singleton', groupLabel: 'Cliente' }

export const useLibraryStore = create<LibraryState>((set, get) => ({
  groups: [],
  processes: [],
  settings: DEFAULT_SETTINGS,
  isLoading: false,
  error: null,

  load: async () => {
    set({ isLoading: true, error: null })
    try {
      // Timeout global: si el cliente Supabase queda colgado (ej. lock interno nunca liberado),
      // isLoading nunca debe quedar en true para siempre — la UI debe poder mostrar error y reintentar.
      const [groups, processes, settings] = await Promise.race([
        Promise.all([
          supabaseGroupRepo.getAll(),
          supabaseProcessRepo.getAll(),
          supabaseSettingsRepo.get(),
        ]),
        new Promise<never>((_, reject) =>
          setTimeout(() => reject(new Error('Timeout: Supabase no respondió en 15s')), 15_000)
        ),
      ])
      set({ groups, processes, settings, isLoading: false, error: null })
    } catch (err) {
      console.error('Error al cargar la biblioteca:', err)
      set({ isLoading: false, error: err instanceof Error ? err.message : 'Error al cargar la biblioteca' })
    }
  },

  createGroup: async (name, userId) => {
    const now = Date.now()
    const group: ProcessGroup = { id: uuidv4(), name, createdAt: now, updatedAt: now }
    await supabaseGroupRepo.save(group, userId)
    set((state) => ({ groups: [...state.groups, group].sort((a, b) => a.name.localeCompare(b.name)) }))
    return group
  },

  deleteGroup: async (id) => {
    await supabaseGroupRepo.delete(id)
    set((state) => ({
      groups: state.groups.filter((g) => g.id !== id),
      processes: state.processes.map((p) => p.groupId === id ? { ...p, groupId: null } : p),
    }))
  },

  deleteProcess: async (id) => {
    await supabaseProcessRepo.delete(id)
    set((state) => ({ processes: state.processes.filter((p) => p.id !== id) }))
  },

  assignProcessToGroup: async (processId, groupId, userId) => {
    const process = get().processes.find((p) => p.id === processId)
    if (!process) return
    const updated = { ...process, groupId, updatedAt: Date.now() }
    await supabaseProcessRepo.save(updated, userId)
    set((state) => ({
      processes: state.processes.map((p) => p.id === processId ? updated : p),
    }))
  },

  getProcessesByGroup: (groupId) => {
    const processes = get().processes
    return groupId === null
      ? processes.filter((p) => p.groupId == null)
      : processes.filter((p) => p.groupId === groupId)
  },

  getLatestSimulation: (processId) => {
    return supabaseSimulationRepo.getLatestByProcess(processId)
  },

  updateSettings: async (updates) => {
    await supabaseSettingsRepo.update(updates)
    set((state) => ({ settings: { ...state.settings, ...updates } }))
  },

  getAllTags: () => {
    const tagSet = new Set<string>()
    get().processes.forEach((p) => p.tags.forEach((t) => tagSet.add(t)))
    return [...tagSet].sort()
  },
}))
