package handlers

import (
	"bytes"
	"context"
	"daemonService/internal/controllers"
	"daemonService/internal/models"
	"daemonService/internal/models/obtencionCodigos"
	"daemonService/internal/services"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"

	"github.com/gorilla/mux"
)

type ServiceHandler struct {
	services    []models.SoapService
	serviceCuis []obtencionCodigos.CuisService
	isCron      bool
}

func NewServiceHandler(services []models.SoapService, serviceCuis []obtencionCodigos.CuisService, isCron bool) *ServiceHandler {
	return &ServiceHandler{
		services:    services,
		serviceCuis: serviceCuis,
		isCron:      isCron,
	}
}

func (h *ServiceHandler) ListServices(w http.ResponseWriter, r *http.Request) {
	serviceNames := make([]string, len(h.services))
	for i, svc := range h.services {
		serviceNames[i] = svc.GetName()
	}

	controllers.RespondWithSuccess(w, http.StatusOK, "Lista de servicios disponibles", serviceNames)
}

func (h *ServiceHandler) RunAllServices(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Minute)
	defer cancel()

	go func() {
		errors := services.RunAllServices(ctx, h.services, h.isCron)
		log.Printf("All services execution completed with %d errors", len(errors))
	}()

	controllers.RespondWithSuccess(w, http.StatusAccepted, "Se inició la ejecución de todos los servicios.", nil)
}

func (h *ServiceHandler) RunService(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	serviceName := vars["name"]

	bodyStr, err := h.readRequestBody(r)
	if err != nil {
		controllers.RespondWithError(w, http.StatusInternalServerError, fmt.Sprintf("Error al leer el request body: %v", err))
		return
	}
	fmt.Println("Request body:", bodyStr)

	service, found := h.findServiceByName(serviceName)
	if !found {
		controllers.RespondWithError(w, http.StatusNotFound, fmt.Sprintf("Servicio no encontrado: %s", serviceName))
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Minute)
	defer cancel()

	respChannel := make(chan interface{}, 1)
	go func() {
		resp := h.executeService(ctx, service, r, serviceName)
		respChannel <- resp
	}()
	resp := <-respChannel
	//controllers.RespondWithSuccess(w, http.StatusAccepted, fmt.Sprintf("Service %s execution initiated", serviceName), resp)
	controllers.RespondWithSuccess(w, 200, fmt.Sprintf("Listado del servicio %s", serviceName), resp)
}

func (h *ServiceHandler) readRequestBody(r *http.Request) (string, error) {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	r.Body.Close()
	r.Body = io.NopCloser(bytes.NewBuffer(body))
	return string(body), nil
}

func (h *ServiceHandler) findServiceByName(name string) (models.SoapService, bool) {
	for _, svc := range h.services {
		if svc.GetName() == name {
			return svc, true
		}
	}
	return nil, false
}

func (h *ServiceHandler) executeService(ctx context.Context, service models.SoapService, r *http.Request, serviceName string) interface{} {
	resp, err := service.Execute(ctx, r, h.isCron)
	if err != nil {
		log.Printf("Error al ejecutar el servicio %s: %v", serviceName, err)
		return nil
	} else {
		log.Printf("Servicio %s ejecutado satisfactoriamente", serviceName)
		return resp
	}
}

// registrar empresa -------------------------------------------------------------------------------------------------------
func (h *ServiceHandler) RunGetCodeService(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	serviceName := vars["obtenerCodigo"]

	service, found := h.findGetCodeByName(serviceName)
	if !found {
		controllers.RespondWithError(w, http.StatusNotFound, fmt.Sprintf("Servicio no encontrado: %s", serviceName))
		return
	}

	ctx, cancel := context.WithTimeout(r.Context(), 5*time.Minute)
	defer cancel()

	respChannel := make(chan interface{}, 1)
	go func() {
		respCuis := h.executeGetCodeService(ctx, service, w, r, serviceName)
		respChannel <- respCuis

	}()
	respCuis := <-respChannel
	controllers.RespondCuisWithSuccess(w, http.StatusAccepted, fmt.Sprintf("Servicio %s ejecucion iniciada", respCuis), respCuis)
}

func (h *ServiceHandler) executeGetCodeService(ctx context.Context, service obtencionCodigos.CuisService, w http.ResponseWriter, r *http.Request, serviceName string) interface{} {
	//var result interface{}
	respCuis, err := service.ExecuteGetCode(ctx, w, r, h.isCron)
	if err != nil {
		log.Printf("Error ejecutando servicio %s: %v", serviceName, err)
		return err // o podrías retornar el error
	} else {
		log.Printf("Servicio %s ejecutado satisfactoriamente", serviceName)
		return respCuis // Aquí retornarías el resultado del servicio
	}
}

func (h *ServiceHandler) findGetCodeByName(name string) (obtencionCodigos.CuisService, bool) {
	for _, svc := range h.serviceCuis {
		if svc.GetName() == name {
			return svc, true
		}
	}
	return nil, false
}
