import { query, getClient } from './db'
import type { PoolClient } from 'pg'

// =====================================================
// TIPOS TYPESCRIPT
// =====================================================

export interface Lote {
  id: string
  codigo: string | null
  tipo: string
  fecha_creado: Date
  lugar_id: number | null
  cantidad_kg: number | null
  meta: Record<string, any> | null
}

export interface Operacion {
  id: string
  tipo: string
  fecha: Date
  lugar_id: number | null
  meta: Record<string, any> | null
}

export interface OperacionLote {
  operacion_id: string
  lote_id: string
  rol: 'input' | 'output'
  cantidad_kg: number | null
}

export interface TrazabilidadRow {
  lote_id: string
  codigo: string | null
  tipo: string
  cantidad_kg: number | null
  operacion_id: string | null
  operacion_tipo: string | null
  profundidad: number
  parent_lote_id: string | null
}

export interface LoteConOrigen extends Lote {
  operacion_id: string | null
  operacion_tipo: string | null
  operacion_fecha: Date | null
}

// =====================================================
// QUERIES PARA LOTES
// =====================================================

/**
 * Obtiene todos los lotes con filtros opcionales
 */
export async function getLotes(filtros?: {
  tipo?: string
  limit?: number
  offset?: number
}): Promise<Lote[]> {
  let sql = 'SELECT * FROM lotes WHERE 1=1'
  const params: any[] = []
  let paramCount = 1

  if (filtros?.tipo) {
    sql += ` AND tipo = $${paramCount}`
    params.push(filtros.tipo)
    paramCount++
  }

  sql += ' ORDER BY fecha_creado DESC'

  if (filtros?.limit) {
    sql += ` LIMIT $${paramCount}`
    params.push(filtros.limit)
    paramCount++
  }

  if (filtros?.offset) {
    sql += ` OFFSET $${paramCount}`
    params.push(filtros.offset)
  }

  const result = await query<Lote>(sql, params)
  return result.rows
}

/**
 * Obtiene un lote por su ID
 */
export async function getLoteById(id: string): Promise<Lote | null> {
  const result = await query<Lote>(
    'SELECT * FROM lotes WHERE id = $1',
    [id]
  )
  return result.rows[0] || null
}

/**
 * Obtiene un lote por su código
 */
export async function getLoteByCodigo(codigo: string): Promise<Lote | null> {
  const result = await query<Lote>(
    'SELECT * FROM lotes WHERE codigo = $1',
    [codigo]
  )
  return result.rows[0] || null
}

/**
 * Crea un nuevo lote
 */
export async function createLote(data: {
  codigo?: string
  tipo: string
  cantidad_kg?: number
  lugar_id?: number
  meta?: Record<string, any>
}): Promise<Lote> {
  const result = await query<Lote>(
    `INSERT INTO lotes (codigo, tipo, cantidad_kg, lugar_id, meta)
     VALUES ($1, $2, $3, $4, $5)
     RETURNING *`,
    [
      data.codigo || null,
      data.tipo,
      data.cantidad_kg || null,
      data.lugar_id || null,
      data.meta ? JSON.stringify(data.meta) : null,
    ]
  )
  return result.rows[0]
}

/**
 * Actualiza un lote existente
 */
export async function updateLote(
  id: string,
  data: Partial<{
    codigo: string | null
    tipo: string
    cantidad_kg: number | null
    lugar_id: number | null
    meta: Record<string, any> | null
  }>
): Promise<Lote | null> {
  const fields: string[] = []
  const params: any[] = []
  let paramCount = 1

  if (data.codigo !== undefined) {
    fields.push(`codigo = $${paramCount}`)
    params.push(data.codigo)
    paramCount++
  }

  if (data.tipo !== undefined) {
    fields.push(`tipo = $${paramCount}`)
    params.push(data.tipo)
    paramCount++
  }

  if (data.cantidad_kg !== undefined) {
    fields.push(`cantidad_kg = $${paramCount}`)
    params.push(data.cantidad_kg)
    paramCount++
  }

  if (data.lugar_id !== undefined) {
    fields.push(`lugar_id = $${paramCount}`)
    params.push(data.lugar_id)
    paramCount++
  }

  if (data.meta !== undefined) {
    fields.push(`meta = $${paramCount}`)
    params.push(data.meta ? JSON.stringify(data.meta) : null)
    paramCount++
  }

  if (fields.length === 0) {
    return getLoteById(id)
  }

  params.push(id)

  const sql = `
    UPDATE lotes
    SET ${fields.join(', ')}
    WHERE id = $${paramCount}
    RETURNING *
  `

  const result = await query<Lote>(sql, params)
  return result.rows[0] || null
}

/**
 * Elimina un lote
 * CUIDADO: Solo debe usarse en casos excepcionales. Preferir marcar como inactivo.
 */
export async function deleteLote(id: string): Promise<boolean> {
  const result = await query(
    'DELETE FROM lotes WHERE id = $1',
    [id]
  )
  return (result.rowCount ?? 0) > 0
}

/**
 * Obtiene todos los lotes con información de su operación de origen
 */
export async function getLotesConOrigen(): Promise<LoteConOrigen[]> {
  const result = await query<LoteConOrigen>(`
    SELECT * FROM vista_lotes_con_origen
    ORDER BY fecha_creado DESC
  `)
  return result.rows
}

// =====================================================
// QUERIES PARA OPERACIONES
// =====================================================

/**
 * Obtiene todas las operaciones con filtros opcionales
 */
export async function getOperaciones(filtros?: {
  tipo?: string
  limit?: number
  offset?: number
}): Promise<Operacion[]> {
  let sql = 'SELECT * FROM operaciones WHERE 1=1'
  const params: any[] = []
  let paramCount = 1

  if (filtros?.tipo) {
    sql += ` AND tipo = $${paramCount}`
    params.push(filtros.tipo)
    paramCount++
  }

  sql += ' ORDER BY fecha DESC'

  if (filtros?.limit) {
    sql += ` LIMIT $${paramCount}`
    params.push(filtros.limit)
    paramCount++
  }

  if (filtros?.offset) {
    sql += ` OFFSET $${paramCount}`
    params.push(filtros.offset)
  }

  const result = await query<Operacion>(sql, params)
  return result.rows
}

/**
 * Obtiene una operación por su ID
 */
export async function getOperacionById(id: string): Promise<Operacion | null> {
  const result = await query<Operacion>(
    'SELECT * FROM operaciones WHERE id = $1',
    [id]
  )
  return result.rows[0] || null
}

/**
 * Obtiene una operación con sus lotes relacionados (inputs y outputs)
 */
export async function getOperacionConLotes(id: string): Promise<{
  operacion: Operacion
  inputs: Array<Lote & { cantidad_kg_usada: number }>
  outputs: Array<Lote & { cantidad_kg_producida: number }>
} | null> {
  const operacion = await getOperacionById(id)
  if (!operacion) return null

  // Obtener lotes de entrada
  const inputsResult = await query<Lote & { cantidad_kg_usada: number }>(`
    SELECT l.*, ol.cantidad_kg as cantidad_kg_usada
    FROM lotes l
    JOIN operacion_lotes ol ON ol.lote_id = l.id
    WHERE ol.operacion_id = $1 AND ol.rol = 'input'
    ORDER BY l.codigo
  `, [id])

  // Obtener lotes de salida
  const outputsResult = await query<Lote & { cantidad_kg_producida: number }>(`
    SELECT l.*, ol.cantidad_kg as cantidad_kg_producida
    FROM lotes l
    JOIN operacion_lotes ol ON ol.lote_id = l.id
    WHERE ol.operacion_id = $1 AND ol.rol = 'output'
    ORDER BY l.codigo
  `, [id])

  return {
    operacion,
    inputs: inputsResult.rows,
    outputs: outputsResult.rows,
  }
}

/**
 * Crea una nueva operación con sus lotes relacionados (TRANSACCIÓN)
 * Esta función asegura que la operación y sus relaciones se creen atómicamente.
 */
export async function createOperacion(data: {
  tipo: string
  fecha?: Date
  lugar_id?: number
  meta?: Record<string, any>
  inputs: Array<{ lote_id: string; cantidad_kg?: number }>
  outputs: Array<{ codigo?: string; tipo: string; cantidad_kg?: number; meta?: Record<string, any> }>
}): Promise<{
  operacion: Operacion
  lotes_creados: Lote[]
}> {
  const client = await getClient()

  try {
    await client.query('BEGIN')

    // 1. Crear la operación
    const operacionResult = await client.query<Operacion>(
      `INSERT INTO operaciones (tipo, fecha, lugar_id, meta)
       VALUES ($1, $2, $3, $4)
       RETURNING *`,
      [
        data.tipo,
        data.fecha || new Date(),
        data.lugar_id || null,
        data.meta ? JSON.stringify(data.meta) : null,
      ]
    )
    const operacion = operacionResult.rows[0]

    // 2. Relacionar lotes de entrada
    for (const input of data.inputs) {
      await client.query(
        `INSERT INTO operacion_lotes (operacion_id, lote_id, rol, cantidad_kg)
         VALUES ($1, $2, 'input', $3)`,
        [operacion.id, input.lote_id, input.cantidad_kg || null]
      )
    }

    // 3. Crear y relacionar lotes de salida
    const lotesCreados: Lote[] = []
    for (const output of data.outputs) {
      const loteResult = await client.query<Lote>(
        `INSERT INTO lotes (codigo, tipo, cantidad_kg, meta)
         VALUES ($1, $2, $3, $4)
         RETURNING *`,
        [
          output.codigo || null,
          output.tipo,
          output.cantidad_kg || null,
          output.meta ? JSON.stringify(output.meta) : null,
        ]
      )
      const lote = loteResult.rows[0]
      lotesCreados.push(lote)

      await client.query(
        `INSERT INTO operacion_lotes (operacion_id, lote_id, rol, cantidad_kg)
         VALUES ($1, $2, 'output', $3)`,
        [operacion.id, lote.id, output.cantidad_kg || null]
      )
    }

    await client.query('COMMIT')

    return {
      operacion,
      lotes_creados: lotesCreados,
    }
  } catch (error) {
    await client.query('ROLLBACK')
    throw error
  } finally {
    client.release()
  }
}

// =====================================================
// QUERIES PARA OPERACION_LOTES
// =====================================================

/**
 * Obtiene todas las relaciones lote-operación para una operación específica
 */
export async function getOperacionLotes(operacionId: string): Promise<OperacionLote[]> {
  const result = await query<OperacionLote>(
    `SELECT * FROM operacion_lotes WHERE operacion_id = $1 ORDER BY rol`,
    [operacionId]
  )
  return result.rows
}

// =====================================================
// QUERIES DE TRAZABILIDAD
// =====================================================

/**
 * Obtiene el historial completo de un lote usando la función recursiva de PostgreSQL
 */
export async function getTrazabilidad(loteId: string): Promise<TrazabilidadRow[]> {
  const result = await query<TrazabilidadRow>(
    'SELECT * FROM get_trazabilidad($1)',
    [loteId]
  )
  return result.rows
}

/**
 * Obtiene estadísticas de un lote (cuántos ancestros tiene, profundidad máxima, etc.)
 */
export async function getEstadisticasLote(loteId: string): Promise<{
  total_ancestros: number
  profundidad_maxima: number
  kg_iniciales: number | null
}> {
  const trazabilidad = await getTrazabilidad(loteId)

  const profundidadMaxima = Math.max(...trazabilidad.map(t => t.profundidad))
  const totalAncestros = trazabilidad.length - 1 // -1 para no contar el lote mismo

  // Buscar lotes de ingreso (profundidad máxima)
  const ingresos = trazabilidad.filter(t => t.profundidad === profundidadMaxima)
  const kgIniciales = ingresos.reduce((sum, t) => sum + (t.cantidad_kg || 0), 0)

  return {
    total_ancestros: totalAncestros,
    profundidad_maxima: profundidadMaxima,
    kg_iniciales: kgIniciales,
  }
}
