/**
 * Tests de migración Dexie v3 → v4 (Sprint 3).
 *
 * Verifican que:
 *  1. Proceso existente conserva todos sus campos y recibe groupId=null, tags=[].
 *  2. Activity existente conserva assignedResources y recibe automatedAssignedResources=[].
 *  3. Settings singleton creado con groupLabel='Cliente' durante el upgrade.
 *  4. El upgrade es idempotente: datos ya migrados no se corrompen.
 *  5. groupId puede ser un UUID válido y es queryable como índice.
 *
 * Usan fake-indexeddb (parcheado en tests/setup.ts).
 */

import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { db } from '@/persistence/db'
import type { Process, ProcessGroup } from '@/domain/types'

// ─── Helper ───────────────────────────────────────────────────────────────────

function makeProcess(id: string, overrides: Partial<Process> = {}): Process {
  return {
    id,
    name: `Proceso ${id}`,
    clientName: 'Cliente Test',
    bpmnXml: '<definitions/>',
    currency: 'USD',
    overheadPercentage: 0.2,
    annualFrequency: 1000,
    analysisHorizonYears: 3,
    automationInvestment: 0,
    createdAt: 1000,
    updatedAt: 2000,
    groupId: null,
    tags: [],
    ...overrides,
  }
}

// ─── Test 1 — Process: lógica del upgrade (puro, sin base de datos) ───────────

describe('Migración Dexie v4 — Process: lógica de upgrade', () => {
  it('proceso v3 (sin groupId ni tags) recibe defaults correctos preservando campos originales', () => {
    const proc: Record<string, unknown> = {
      id: 'p1',
      name: 'Proceso Legado',
      clientName: 'Cliente A',
      bpmnXml: '<definitions/>',
      currency: 'PYG',
      overheadPercentage: 0.15,
      annualFrequency: 500,
      analysisHorizonYears: 5,
      automationInvestment: 50_000,
      createdAt: 1000,
      updatedAt: 2000,
      // Sin groupId ni tags — formato v3
    }

    // Simular callback del upgrade
    if (proc.groupId === undefined) proc.groupId = null
    if (!Array.isArray(proc.tags))  proc.tags = []

    // Campos originales intactos
    expect(proc.id).toBe('p1')
    expect(proc.name).toBe('Proceso Legado')
    expect(proc.clientName).toBe('Cliente A')
    expect(proc.currency).toBe('PYG')
    expect(proc.overheadPercentage).toBe(0.15)
    expect(proc.annualFrequency).toBe(500)
    expect(proc.analysisHorizonYears).toBe(5)
    expect(proc.automationInvestment).toBe(50_000)
    expect(proc.createdAt).toBe(1000)
    expect(proc.updatedAt).toBe(2000)
    // Nuevos campos con defaults correctos
    expect(proc.groupId).toBeNull()
    expect(proc.tags).toEqual([])
  })
})

// ─── Test 2 — Activity: lógica del upgrade (puro, sin base de datos) ─────────

describe('Migración Dexie v4 — Activity: lógica de upgrade', () => {
  it('activity v3 con assignedResources poblados recibe automatedAssignedResources=[]', () => {
    const act: Record<string, unknown> = {
      id: 'a1',
      processId: 'p1',
      bpmnElementId: 'task_a1',
      name: 'Tarea A',
      type: 'task',
      directCostFixed: 500,
      executionTimeMinutes: 60,
      assignedResources: [{ resourceId: 'r1', utilizationMinutes: 30, units: 1 }],
      automatable: true,
      automatedCostFixed: 200,
      automatedTimeMinutes: 15,
      // Sin automatedAssignedResources — formato v3
    }

    // Simular callback del upgrade
    if (!Array.isArray(act.automatedAssignedResources)) {
      act.automatedAssignedResources = []
    }

    // assignedResources no fue alterado
    expect(act.assignedResources).toEqual([{ resourceId: 'r1', utilizationMinutes: 30, units: 1 }])
    // campos de automatización intactos
    expect(act.automatable).toBe(true)
    expect(act.automatedCostFixed).toBe(200)
    expect(act.automatedTimeMinutes).toBe(15)
    // nuevo campo vacío
    expect(act.automatedAssignedResources).toEqual([])
  })
})

// ─── Test 3 — Settings singleton (integración) ───────────────────────────────

describe('Migración Dexie v4 — Settings singleton', () => {
  beforeEach(async () => {
    await db.open()
  })

  afterEach(async () => {
    await db.close()
  })

  it('singleton creado con groupLabel=Cliente durante el upgrade', async () => {
    const settings = await db.settings.get('singleton')
    expect(settings).toBeDefined()
    expect(settings!.id).toBe('singleton')
    expect(settings!.groupLabel).toBe('Cliente')
  })
})

// ─── Test 4 — Idempotencia (puro, sin base de datos) ─────────────────────────

describe('Migración Dexie v4 — Idempotencia', () => {
  it('upgrade no corrompe proceso ya migrado (groupId y tags con valores)', () => {
    const proc: Record<string, unknown> = {
      id: 'p2',
      groupId: null,
      tags: ['facturación', 'BPMN'],
    }

    // Simular upgrade (idempotente)
    if (proc.groupId === undefined) proc.groupId = null
    if (!Array.isArray(proc.tags))  proc.tags = []

    expect(proc.groupId).toBeNull()
    expect(proc.tags).toEqual(['facturación', 'BPMN'])
  })

  it('upgrade no corrompe activity ya migrada (automatedAssignedResources poblado)', () => {
    const act: Record<string, unknown> = {
      id: 'a2',
      automatedAssignedResources: [{ resourceId: 'r2', utilizationMinutes: 10, units: 2 }],
    }

    if (!Array.isArray(act.automatedAssignedResources)) {
      act.automatedAssignedResources = []
    }

    expect(act.automatedAssignedResources).toEqual([
      { resourceId: 'r2', utilizationMinutes: 10, units: 2 },
    ])
  })
})

// ─── Test 5 — groupId como UUID válido (integración) ─────────────────────────

describe('Migración Dexie v4 — groupId como UUID válido', () => {
  beforeEach(async () => {
    await db.open()
    await db.transaction('rw', [db.processes, db.groups], async () => {
      await db.processes.clear()
      await db.groups.clear()
    })
  })

  afterEach(async () => {
    await db.close()
  })

  it('proceso con groupId persiste y es queryable por el índice groupId', async () => {
    const group: ProcessGroup = {
      id: 'group-abc-123',
      name: 'Grupo Test',
      createdAt: 1000,
      updatedAt: 2000,
    }
    await db.groups.put(group)

    const proc: Process = makeProcess('p3', {
      groupId: 'group-abc-123',
      tags: ['test'],
    })
    await db.processes.put(proc)

    // El campo persiste
    const retrieved = await db.processes.get('p3')
    expect(retrieved!.groupId).toBe('group-abc-123')
    expect(retrieved!.tags).toEqual(['test'])

    // El índice funciona para query por grupo
    const byGroup = await db.processes.where('groupId').equals('group-abc-123').toArray()
    expect(byGroup).toHaveLength(1)
    expect(byGroup[0].id).toBe('p3')
  })

  it('proceso con groupId=null persiste y es recuperable', async () => {
    const proc: Process = makeProcess('p4', { groupId: null, tags: [] })
    await db.processes.put(proc)

    const retrieved = await db.processes.get('p4')
    expect(retrieved).toBeDefined()
    expect(retrieved!.groupId).toBeNull()

    // Verificar con filter (Dexie no acepta null en .equals())
    const sinGrupo = await db.processes.filter((p) => p.groupId == null).toArray()
    expect(sinGrupo.some((p) => p.id === 'p4')).toBe(true)
  })
})
