/**
 * @internal
 *
 * Repositorios de acceso a IndexedDB (Dexie).
 *
 * REGLA ARQUITECTÓNICA: este módulo solo debe importarse desde `src/store/**`.
 * Las features y hooks deben mutar datos a través de los stores (useProcessStore,
 * useSimulationStore), que garantizan la invalidación del resultado de simulación
 * cuando los datos cambian.
 *
 * Excepciones permitidas (ver eslint.config.js):
 *   - src/features/import/** → operación de importación inicial (no hay simulación previa)
 *   - src/features/report/useReportData.ts → solo lectura, sin mutaciones
 *   - tests/** → acceso directo controlado en contexto de testing
 *
 * Cualquier import desde fuera de estos paths falla `npm run lint`.
 */

import { db } from './db'
import type { Process, Activity, GatewayConfig, Resource, Simulation, ProcessGroup, AppSettings } from '@/domain/types'

// ─── Process ─────────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/process-store.ts */
export const processRepo = {
  async save(process: Process): Promise<void> {
    await db.processes.put(process)
  },

  async getById(id: string): Promise<Process | undefined> {
    return db.processes.get(id)
  },

  async getAll(): Promise<Process[]> {
    return db.processes.orderBy('updatedAt').reverse().toArray()
  },

  async delete(id: string): Promise<void> {
    await db.transaction('rw', [db.processes, db.activities, db.gateways, db.resources, db.simulations], async () => {
      await db.processes.delete(id)
      await db.activities.where('processId').equals(id).delete()
      await db.gateways.where('processId').equals(id).delete()
      await db.resources.where('processId').equals(id).delete()
      await db.simulations.where('processId').equals(id).delete()
    })
  },
}

// ─── Activity ─────────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/process-store.ts */
export const activityRepo = {
  async save(activity: Activity): Promise<void> {
    await db.activities.put(activity)
  },

  async saveMany(activities: Activity[]): Promise<void> {
    await db.activities.bulkPut(activities)
  },

  async getByProcess(processId: string): Promise<Activity[]> {
    return db.activities.where('processId').equals(processId).toArray()
  },

  async getByBpmnElementId(processId: string, bpmnElementId: string): Promise<Activity | undefined> {
    return db.activities
      .where('[processId+bpmnElementId]')
      .equals([processId, bpmnElementId])
      .first()
  },

  async deleteByProcess(processId: string): Promise<void> {
    await db.activities.where('processId').equals(processId).delete()
  },
}

// ─── GatewayConfig ────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/process-store.ts */
export const gatewayRepo = {
  async save(gateway: GatewayConfig): Promise<void> {
    await db.gateways.put(gateway)
  },

  async saveMany(gateways: GatewayConfig[]): Promise<void> {
    await db.gateways.bulkPut(gateways)
  },

  async getByProcess(processId: string): Promise<GatewayConfig[]> {
    return db.gateways.where('processId').equals(processId).toArray()
  },

  async deleteByProcess(processId: string): Promise<void> {
    await db.gateways.where('processId').equals(processId).delete()
  },
}

// ─── Resource ─────────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/process-store.ts */
export const resourceRepo = {
  async save(resource: Resource): Promise<void> {
    await db.resources.put(resource)
  },

  async getByProcess(processId: string): Promise<Resource[]> {
    return db.resources.where('processId').equals(processId).toArray()
  },

  async delete(id: string): Promise<void> {
    await db.resources.delete(id)
  },

  async deleteByProcess(processId: string): Promise<void> {
    await db.resources.where('processId').equals(processId).delete()
  },
}

// ─── Simulation ───────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/simulation-store.ts */
export const simulationRepo = {
  async save(simulation: Simulation): Promise<void> {
    await db.simulations.put(simulation)
  },

  async getLatestByProcess(processId: string): Promise<Simulation | undefined> {
    // Ordena por executedAt descendente para retornar la simulación más reciente.
    // No se usa .last() porque el primary key es UUID (no ordenado cronológicamente).
    const all = await db.simulations
      .where('processId')
      .equals(processId)
      .toArray()
    if (!all.length) return undefined
    return all.reduce((latest, sim) => sim.executedAt > latest.executedAt ? sim : latest)
  },

  async getByProcess(processId: string): Promise<Simulation[]> {
    return db.simulations.where('processId').equals(processId).toArray()
  },
}

// ─── ProcessGroup ─────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/library-store.ts */
export const groupRepo = {
  async save(group: ProcessGroup): Promise<void> {
    await db.groups.put(group)
  },

  async getAll(): Promise<ProcessGroup[]> {
    return db.groups.orderBy('name').toArray()
  },

  async getById(id: string): Promise<ProcessGroup | undefined> {
    return db.groups.get(id)
  },

  async delete(id: string): Promise<void> {
    // Procesos del grupo pasan a "Sin clasificar" (groupId = null)
    await db.transaction('rw', [db.groups, db.processes], async () => {
      await db.processes.where('groupId').equals(id).modify({ groupId: null })
      await db.groups.delete(id)
    })
  },
}

// ─── AppSettings ──────────────────────────────────────────────────────────────

/** @internal Solo llamar desde src/store/library-store.ts */
export const settingsRepo = {
  async get(): Promise<AppSettings> {
    const s = await db.settings.get('singleton')
    return s ?? { id: 'singleton', groupLabel: 'Cliente' }
  },

  async update(updates: Partial<Omit<AppSettings, 'id'>>): Promise<void> {
    await db.settings.update('singleton', updates)
  },
}
