package sr

import (
	"fmt"
	"strconv"
	"strings"
	"system-altrak/internal/domain"
	"system-altrak/internal/dto"
	"system-altrak/internal/middleware"
	"system-altrak/pkg/utils"

	"github.com/gofiber/fiber/v2"
)

type Handler struct {
	service SRService
}

func NewHandler(s SRService) *Handler {
	return &Handler{service: s}
}

func (h *Handler) CreateSR(c *fiber.Ctx) error {
	var req dto.ServiceRequisitionRequest
	if err := c.BodyParser(&req); err != nil {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid requisition data structure format")
	}

	middleware.SanitizeStruct(&req)

	result := utils.ValidateStructDetailed(req)
	if !result.IsValid {
		return utils.ValidationErrorResponse(c, "Validation failed", result.Errors)
	}

	sr := buildServiceRequisition(req)

	branchID, _ := c.Locals("current_branch_id").(uint)
	if err := h.service.CreateAuthorization(&sr, branchID); err != nil {
		return utils.InternalErrorResponse(c, err.Error())
	}
	return utils.SuccessResponse(c, "Service Requisition synchronized successfully", sr)
}

func (h *Handler) UpdateSR(c *fiber.Ctx) error {
	id, err := utils.ParseParamID(c, "id")
	if err != nil {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid ID parameter")
	}

	var req dto.ServiceRequisitionRequest
	if err := c.BodyParser(&req); err != nil {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid requisition data structure format")
	}

	middleware.SanitizeStruct(&req)

	result := utils.ValidateStructDetailed(req)
	if !result.IsValid {
		return utils.ValidationErrorResponse(c, "Validation failed", result.Errors)
	}

	sr := buildServiceRequisition(req)
	sr.ID = id

	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)
	if err := h.service.UpdateAuthorization(&sr, branchID, role); err != nil {
		return utils.InternalErrorResponse(c, err.Error())
	}
	return utils.SuccessResponse(c, "Service Requisition updated successfully", sr)
}

func (h *Handler) ListSRs(c *fiber.Ctx) error {
	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)
	list, err := h.service.ListSRs(branchID, role)
	if err != nil {
		return utils.InternalErrorResponse(c, "Failed to load SR list")
	}
	return utils.SuccessResponse(c, "OK", list)
}

func (h *Handler) GetSR(c *fiber.Ctx) error {
	id, err := utils.ParseParamID(c, "id")
	if err != nil {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid ID parameter")
	}
	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)
	sr, err := h.service.GetSR(id, branchID, role)
	if err != nil {
		return utils.NotFoundResponse(c, "Service Requisition")
	}
	return utils.SuccessResponse(c, "Service Requisition localized successfully", sr)
}

func (h *Handler) ExportExcel(c *fiber.Ctx) error {
	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)

	tab := utils.NormalizeTab("sr", c.Query("tab", "all"), "all")
	if tab != "all" && tab != "draft" && tab != "completed" {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid tab filter. Allowed values: all, draft, completed (or verified alias)")
	}

	search := strings.TrimSpace(c.Query("search", ""))

	reader, err := h.service.ExportSRToExcel(branchID, role, tab, search)
	if err != nil {
		return utils.InternalErrorResponse(c, err.Error())
	}

	c.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	c.Set("Content-Disposition", "attachment; filename=Service_Requisition_List.xlsx")
	return c.SendStream(reader)
}

func (h *Handler) DownloadExport(c *fiber.Ctx) error {
	idRaw := strings.TrimSpace(c.Query("id"))
	if idRaw == "" {
		idRaw = strings.TrimSpace(c.Params("id"))
	}
	if idRaw == "" {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid ID parameter")
	}

	parsedID, err := strconv.ParseUint(idRaw, 10, 32)
	if err != nil || parsedID == 0 {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid ID parameter")
	}
	id := uint(parsedID)

	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)
	buf, ref, err := h.service.GeneratePDF(id, branchID, role)
	if err != nil {
		return utils.InternalErrorResponse(c, err.Error())
	}

	c.Set("Content-Type", "application/pdf")
	c.Set("Content-Disposition", fmt.Sprintf("attachment; filename=REQ-%s.pdf", ref))
	return c.Send(buf)
}

func (h *Handler) DeleteSR(c *fiber.Ctx) error {
	id, err := utils.ParseParamID(c, "id")
	if err != nil {
		return utils.ErrorResponse(c, fiber.StatusBadRequest, "Invalid ID parameter")
	}
	branchID, _ := c.Locals("current_branch_id").(uint)
	role, _ := c.Locals("role").(string)
	if err := h.service.DeleteSR(id, branchID, role); err != nil {
		return utils.InternalErrorResponse(c, "Failed to purge service requisition")
	}
	return utils.SuccessResponse(c, "Service Requisition purged successfully", nil)
}

func buildServiceRequisition(req dto.ServiceRequisitionRequest) domain.ServiceRequisition {
	return domain.ServiceRequisition{
		ChargeTo:          req.ChargeTo,
		CC:                req.CC,
		Customer:          req.Customer,
		EquipmentLocation: req.EquipmentLocation,
		SalesAgreementNo:  req.SalesAgreementNo,
		Description:       req.Description,
		Status:            req.Status,
		Checklist:         buildServiceRequisitionChecklist(req.Checklist),
		Equipments:        buildServiceRequisitionEquipments(req.Equipments),
	}
}

func buildServiceRequisitionChecklist(req *dto.ServiceRequisitionChecklistRequest) *domain.ServiceRequisitionChecklist {
	if req == nil {
		return nil
	}

	return &domain.ServiceRequisitionChecklist{
		PreDeliveryCheck:    req.PreDeliveryCheck,
		DeliveryCheck:       req.DeliveryCheck,
		PeriodicMaintenance: req.PeriodicMaintenance,
		Repair:              req.Repair,
		Assemble:            req.Assemble,
		Recondition:         req.Recondition,
		Training:            req.Training,
		Other:               req.Other,
		Unit:                req.Unit,
		PanelCabel:          req.PanelCabel,
		FuelTank:            req.FuelTank,
		ExhaustSystem:       req.ExhaustSystem,
		FrontAttachment:     req.FrontAttachment,
		RearAttachment:      req.RearAttachment,
		Manuals:             req.Manuals,
		Others:              req.Others,
	}
}

func buildServiceRequisitionEquipments(req []dto.ServiceRequisitionEquipmentRequest) []domain.ServiceRequisitionEquipment {
	if len(req) == 0 {
		return nil
	}

	equipments := make([]domain.ServiceRequisitionEquipment, 0, len(req))
	for _, item := range req {
		equipments = append(equipments, domain.ServiceRequisitionEquipment{
			No:           item.No,
			Brand:        item.Brand,
			Model:        item.Model,
			SerialNumber: item.SerialNumber,
			Qty:          item.Qty,
			MHPNo:        item.MHPNo,
			TableBlock:   item.TableBlock,
		})
	}

	return equipments
}
