package api

import (
	"context"
	"net/http"
	"time"

	"api-soap-facturacion/internal/config"
	"api-soap-facturacion/internal/database"
	"api-soap-facturacion/internal/logger"
	"api-soap-facturacion/internal/soap"
	"api-soap-facturacion/internal/soap/api2"
	//"api-soap-facturacion/pkg/errors"
	soapPkg "api-soap-facturacion/pkg/soap"
	"github.com/gin-gonic/gin"
	"github.com/jmoiron/sqlx"
)

// Handler maneja las solicitudes HTTP
type Handler struct {
	config      *config.Config
	logger      logger.Logger
	db          *sqlx.DB
	soapClients *soap.Clients
}

// NewHandler crea un nuevo handler
func NewHandler(cfg *config.Config, log logger.Logger, db *sqlx.DB, soapClients *soap.Clients) *Handler {
	return &Handler{
		config:      cfg,
		logger:      log,
		db:          db,
		soapClients: soapClients,
	}
}

// HealthCheck verifica el estado de la aplicación
func (h *Handler) HealthCheck(c *gin.Context) {
	// Verificar conexión a la base de datos
	dbErr := h.db.Ping()

	status := "ok"
	statusCode := http.StatusOK
	if dbErr != nil {
		status = "degraded"
		statusCode = http.StatusServiceUnavailable
	}

	c.JSON(statusCode, gin.H{
		"status":    status,
		"timestamp": time.Now().Format(time.RFC3339),
		"version":   "1.0.0",
		"services": gin.H{
			"database": dbErr == nil,
		},
	})
}

// API2ProcessTransaction maneja solicitudes de transacción para API2
func (h *Handler) API2ProcessTransaction(c *gin.Context) {
	// Obtener logger
	log := getLogger(c)

	// Obtener contexto con timeout
	ctx, cancel := context.WithTimeout(c.Request.Context(), h.config.App.RequestTimeout*time.Second)
	defer cancel()

	// Agregar ID de solicitud al contexto
	requestID := c.GetHeader("X-Request-ID")
	ctx = context.WithValue(ctx, soapPkg.RequestIDKey{}, requestID)

	// Parsear solicitud
	var requestData struct {
		TransactionID string  `json:"transactionId" binding:"required"`
		AccountID     string  `json:"accountId" binding:"required"`
		Amount        float64 `json:"amount" binding:"required"`
		Currency      string  `json:"currency" binding:"required"`
		Description   string  `json:"description"`
	}

	if err := c.ShouldBindJSON(&requestData); err != nil {
		log.Error("Error al parsear solicitud", logger.NewField("error", err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Error al parsear solicitud",
		})
		return
	}

	// Crear solicitud SOAP
	request := api2.TransactionRequest{
		Namespace:     "http://api2.example.com/soap",
		TransactionID: requestData.TransactionID,
		AccountID:     requestData.AccountID,
		Amount:        requestData.Amount,
		Currency:      requestData.Currency,
		Description:   requestData.Description,
	}

	// Llamar a API2
	response, err := h.soapClients.API2.ProcessTransaction(ctx, request)
	if err != nil {
		log.Error("Error al llamar a API2", logger.NewField("error", err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Error al realizar solicitud a API2",
		})
		return
	}

	// Retornar respuesta
	c.JSON(http.StatusOK, gin.H{
		"resultCode":       response.ResultCode,
		"resultMessage":    response.ResultMessage,
		"transactionId":    response.TransactionID,
		"confirmationCode": response.ConfirmationCode,
		"timestamp":        response.Timestamp,
	})
}

// GetAllParameters retorna todos los parámetros
func (h *Handler) GetAllParameters(c *gin.Context) {
	// Obtener logger
	log := getLogger(c)

	// Consultar parámetros
	var params []database.Parameter
	query := `SELECT id, key, value, description, active, created_at, updated_at FROM parameters`

	err := h.db.Select(&params, query)
	if err != nil {
		log.Error("Error al obtener parámetros", logger.NewField("error", err))
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Error al obtener parámetros",
		})
		return
	}

	// Retornar parámetros
	c.JSON(http.StatusOK, params)
}

// GetParameter retorna un parámetro por clave
func (h *Handler) GetParameter(c *gin.Context) {
	// Obtener logger
	log := getLogger(c)

	// Obtener clave
	key := c.Param("key")

	// Consultar parámetro
	var param database.Parameter
	query := `SELECT id, key, value, description, active, created_at, updated_at FROM parameters WHERE key = $1`

	err := h.db.Get(&param, query, key)
	if err != nil {
		log.Error("Error al obtener parámetro",
			logger.NewField("error", err),
			logger.NewField("key", key),
		)
		c.JSON(http.StatusNotFound, gin.H{
			"error": "Parámetro no encontrado",
		})
		return
	}

	// Retornar parámetro
	c.JSON(http.StatusOK, param)
}

// UpdateParameter actualiza un parámetro
func (h *Handler) UpdateParameter(c *gin.Context) {
	// Obtener logger
	log := getLogger(c)

	// Obtener clave
	key := c.Param("key")

	// Parsear solicitud
	var requestData struct {
		Value       string `json:"value" binding:"required"`
		Description string `json:"description"`
		Active      bool   `json:"active"`
	}

	if err := c.ShouldBindJSON(&requestData); err != nil {
		log.Error("Error al parsear solicitud", logger.NewField("error", err))
		c.JSON(http.StatusBadRequest, gin.H{
			"error": "Error al parsear solicitud",
		})
		return
	}

	// Actualizar parámetro
	query := `
		UPDATE parameters 
		SET value = $1, description = $2, active = $3, updated_at = NOW() 
		WHERE key = $4
		RETURNING id, key, value, description, active, created_at, updated_at
	`

	var param database.Parameter
	err := h.db.Get(&param, query,
		requestData.Value,
		requestData.Description,
		requestData.Active,
		key,
	)

	if err != nil {
		log.Error("Error al actualizar parámetro",
			logger.NewField("error", err),
			logger.NewField("key", key),
		)
		c.JSON(http.StatusInternalServerError, gin.H{
			"error": "Error al actualizar parámetro",
		})
		return
	}

	// Retornar parámetro actualizado
	c.JSON(http.StatusOK, param)
}

// getLogger obtiene el logger del contexto
func getLogger(c *gin.Context) logger.Logger {
	if log, exists := c.Get("logger"); exists {
		return log.(logger.Logger)
	}
	// Si no hay logger en el contexto, retornar un logger por defecto
	return &defaultLogger{}
}

// defaultLogger es un logger por defecto
type defaultLogger struct{}

func (l *defaultLogger) Debug(msg string, fields ...logger.Field)  {}
func (l *defaultLogger) Info(msg string, fields ...logger.Field)   {}
func (l *defaultLogger) Warn(msg string, fields ...logger.Field)   {}
func (l *defaultLogger) Error(msg string, fields ...logger.Field)  {}
func (l *defaultLogger) Fatal(msg string, fields ...logger.Field)  {}
func (l *defaultLogger) With(fields ...logger.Field) logger.Logger { return l }
