// repository/cuis_repo.go
package repositories

import (
	"context"
	"daemonService/internal/models"
	"daemonService/internal/models/obtencionCodigos/request"
	"daemonService/internal/models/obtencionCodigos/response"
	srv "daemonService/internal/services"
	"daemonService/internal/utils"
	"database/sql"
	"fmt"
	"net/http"
)

type CuisRepository struct {
	CuisService  *srv.CuisService
	ModelService models.ServiceModel
}

// instanciamos cuisRepository
func NewCuisRepository(model models.ServiceModel, cuisService *srv.CuisService) *CuisRepository {
	return &CuisRepository{ModelService: model, CuisService: cuisService}
}

func (cr *CuisRepository) CrearNuevoRegistro(ctx context.Context, request request.SolicitudCuisCufd, codigoCuis string, fechaVigencia string, transaccion bool, cufdData response.SoapBodyCufd) error {
	// Obtener la hora actual de Bolivia
	currentBolivia, err := utils.GetCurrentBoliviaTime()
	if err != nil {
		return fmt.Errorf("error al obtener hora de Bolivia: %w", err)
	}

	// Iniciar transacción
	tx, err := cr.ModelService.DB.BeginTx(ctx, nil)
	if err != nil {
		cr.CuisService.LogMessage("Error al iniciar transacción: %v", err)
		return utils.ErrorCustom(http.StatusBadRequest, "Error al iniciar transacción")
	}
	defer func() {
		if err != nil {
			tx.Rollback()
		}
	}()

	// Consulta corregida para manejar actualizaciones de CUIS y CUFD
	query := `
       WITH empresa AS (
          -- Inserta o actualiza empresa usando la clave natural
          INSERT INTO registroEmpresa (
             codigo_ambiente,
             codigo_modalidad,
             codigo_punto_venta,
             codigo_sistema,
             codigo_sucursal,
             nit,
             token_key,
             token_value,
             nombre_archivo_certificado,
             nombre_archivo_clave_privada
          ) 
          VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
          ON CONFLICT (nit, codigo_sucursal, codigo_punto_venta) 
          DO UPDATE SET
             codigo_ambiente = EXCLUDED.codigo_ambiente,
             codigo_modalidad = EXCLUDED.codigo_modalidad,
             codigo_sistema = EXCLUDED.codigo_sistema,
             token_key = EXCLUDED.token_key,
             token_value = EXCLUDED.token_value,
             nombre_archivo_certificado = EXCLUDED.nombre_archivo_certificado,
             nombre_archivo_clave_privada = EXCLUDED.nombre_archivo_clave_privada,
             fecha_actualizacion = CURRENT_TIMESTAMP
          RETURNING id
       ),
       cuis_existente AS (
          -- Busca si existe un CUIS para la empresa
          SELECT id, cuis, registro_empresa_id
          FROM cuis 
          WHERE registro_empresa_id = (SELECT id FROM empresa)
          LIMIT 1
       ),
       cuis_actualizado AS (
          -- Si existe CUIS pero es diferente, actualízalo
          UPDATE cuis
          SET 
             cuis = $11,
             fecha_vigencia = $12,
             transaccion = $13,
             fecha_actualizacion = CURRENT_TIMESTAMP
          FROM cuis_existente
          WHERE cuis.id = cuis_existente.id
          AND (cuis_existente.cuis <> $11 OR cuis_existente.cuis IS NULL)
          RETURNING cuis.id, cuis.registro_empresa_id
       ),
       cuis_insertado AS (
          -- Si no existe CUIS, inserta uno nuevo
          INSERT INTO cuis (
             cuis, 
             fecha_vigencia, 
             transaccion, 
             fecha_creacion, 
             registro_empresa_id
          )
          SELECT 
             $11, $12, $13, $14, id 
          FROM empresa
          WHERE NOT EXISTS (SELECT 1 FROM cuis_existente)
          RETURNING id, registro_empresa_id
       ),
       cuis_final AS (
          -- Usa el CUIS actualizado si existe
          SELECT id, registro_empresa_id FROM cuis_actualizado
          UNION ALL
          -- O el insertado si no había uno previo
          SELECT id, registro_empresa_id FROM cuis_insertado
          UNION ALL
          -- O el existente si no se actualizó (porque era igual)
          SELECT id, registro_empresa_id FROM cuis_existente
          WHERE NOT EXISTS (SELECT 1 FROM cuis_actualizado)
          AND EXISTS (SELECT 1 FROM cuis_existente)
       ),
       cufd_existente AS (
          -- Busca si existe un CUFD para el CUIS actual
          SELECT c.id, c.codigo, c.codigo_control, c.direccion, c.cuis_id
          FROM cufd c
          JOIN cuis_final cf ON c.cuis_id = cf.id
          LIMIT 1
       ),
       cufd_actualizado AS (
          -- Si existe CUFD pero algún valor es diferente, actualízalo
          UPDATE cufd
          SET 
             codigo = $15,
             codigo_control = $16,
             direccion = $17,
             fecha_vigencia = $18,
             transaccion = $19,
             fecha_actualizacion = CURRENT_TIMESTAMP
          FROM cufd_existente, cuis_final
          WHERE cufd.id = cufd_existente.id
          AND cufd.cuis_id = cuis_final.id
          AND (
             cufd_existente.codigo <> $15 OR
             cufd_existente.codigo_control <> $16 OR
             cufd_existente.direccion <> $17 OR
             cufd_existente.codigo IS NULL
          )
          RETURNING (SELECT registro_empresa_id FROM cuis_final LIMIT 1) as registro_id
       ),
       cufd_insertado AS (
          -- Si no existe CUFD, inserta uno nuevo
          INSERT INTO cufd (
             codigo, 
             codigo_control, 
             direccion, 
             fecha_vigencia, 
             transaccion, 
             fecha_creacion, 
             cuis_id
          )
          SELECT 
             $15, $16, $17, $18, $19, $20, id 
          FROM cuis_final
          WHERE NOT EXISTS (SELECT 1 FROM cufd_existente)
          RETURNING (SELECT registro_empresa_id FROM cuis_final LIMIT 1) as registro_id
       ),
       resultado_final AS (
          -- Usa el resultado de cualquiera de las operaciones
          SELECT registro_id FROM cufd_actualizado
          UNION ALL
          SELECT registro_id FROM cufd_insertado
          UNION ALL
          -- O usa el ID de empresa si el CUFD ya existía y no cambió
          SELECT registro_empresa_id as registro_id FROM cuis_final
          WHERE NOT EXISTS (SELECT 1 FROM cufd_actualizado)
          AND NOT EXISTS (SELECT 1 FROM cufd_insertado)
          AND EXISTS (SELECT 1 FROM cufd_existente)
          LIMIT 1
       )
       SELECT registro_id FROM resultado_final LIMIT 1;
    `

	var registroID int
	err = tx.QueryRowContext(
		ctx,
		query,
		// Parámetros para registroEmpresa
		request.CodigoAmbiente,
		request.CodigoModalidad,
		request.CodigoPuntoVenta,
		request.CodigoSistema,
		request.CodigoSucursal,
		request.Nit,
		request.KeyToken,
		request.ValueToken,
		request.NombreArchivoCertificado,  // Nuevo parámetro
		request.NombreArchivoClavePrivada, // Nuevo parámetro
		// Parámetros para cuis
		codigoCuis,
		fechaVigencia,
		transaccion,
		currentBolivia,
		// Parámetros para cufd
		cufdData.Response.Respuesta.Codigo,
		cufdData.Response.Respuesta.CodigoControl,
		cufdData.Response.Respuesta.Direccion,
		cufdData.Response.Respuesta.FechaVigencia,
		cufdData.Response.Respuesta.Transaccion,
		currentBolivia,
	).Scan(&registroID)

	if err != nil {
		cr.CuisService.LogMessage("Error al insertar/actualizar registros: %v", err)
		return utils.ErrorCustom(http.StatusInternalServerError, "Error al procesar los registros en la base de datos")
	}

	// Confirmar la transacción
	if err = tx.Commit(); err != nil {
		cr.CuisService.LogMessage("Error al confirmar transacción: %v", err)
		return utils.ErrorCustom(http.StatusInternalServerError, "Error al confirmar transacción")
	}

	return nil
}

// actualiza un registro existente de empresa y CUIS
func (cr *CuisRepository) ActualizarRegistro(ctx context.Context, empresaID int, cuisID int64, req request.SolicitudCuisCufd, respRaw interface{}) error {
	currentBolivia, err := utils.GetCurrentBoliviaTime()
	if err != nil {
		return err
	}

	// 1. Iniciar tx
	tx, err := cr.ModelService.DB.BeginTx(ctx, nil)
	if err != nil {
		cr.CuisService.LogMessage("Error iniciar transacción: %d", err)
		return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error iniciar transacción"))
	}
	defer tx.Rollback()

	// 2. Parsear respuesta
	nuevoCuis, fechaVigencia, _, err := utils.ParseSoapCuisResponse(respRaw)
	if err != nil {
		cr.CuisService.LogMessage("Error al parsear la respuesta: %d", err)
		return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al parsear la respuesta"))
	}

	// 3. Actualizar empresa
	_, err = tx.ExecContext(ctx, `
       UPDATE registroEmpresa
       SET codigo_ambiente=$1, codigo_modalidad=$2, codigo_punto_venta=$3,
           codigo_sistema=$4, codigo_sucursal=$5, nit=$6, fecha_actualizacion=$7
       WHERE id=$8
   `,
		req.CodigoAmbiente, req.CodigoModalidad, req.CodigoPuntoVenta,
		req.CodigoSistema, req.CodigoSucursal, req.Nit, currentBolivia, empresaID,
	)
	if err != nil {
		cr.CuisService.LogMessage("Error al actualizar registroEmpresa: %d", err)
		return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al actualizar la tabla registroEmpresa"))
	}

	// 4. Comprobar si hay un CUIS existente
	var existingCuis string
	err = tx.QueryRowContext(ctx, `SELECT cuis FROM cuis WHERE id=$1 AND registro_empresa_id=$2`, cuisID, empresaID).Scan(&existingCuis)
	if err != nil && err != sql.ErrNoRows {
		cr.CuisService.LogMessage("Error al consultar cuis existente: %d", err)
		return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al consultar cuis existente"))
	}

	// 5. Insertar o actualizar
	if err == sql.ErrNoRows {
		_, err = tx.ExecContext(ctx, `INSERT INTO cuis (registro_empresa_id, cuis) VALUES ($1, $2)`, empresaID, nuevoCuis)
		if err != nil {
			cr.CuisService.LogMessage("Error al insertar nuevo cuis: %d", err)
			return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al insertar nuevo cuis"))
		}
	} else if existingCuis != nuevoCuis {
		_, err = tx.ExecContext(ctx, `UPDATE cuis SET cuis=$1, fecha_vigencia=$2, fecha_actualizacion=$3 WHERE id=$4`,
			nuevoCuis, fechaVigencia, currentBolivia, cuisID)
		if err != nil {
			cr.CuisService.LogMessage("Error al actualizar cuis existente: %d", err)
			return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al actualizar cuis existente"))
		}
	}

	// 6. Confirmar tx
	if err := tx.Commit(); err != nil {
		cr.CuisService.LogMessage("Error al confirmar transacción: %d", err)
		return fmt.Errorf("%w", utils.ErrorCustom(http.StatusBadRequest, "Error al confirmar transacción"))
	}
	return nil
}
