package handlers

import (
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"

	flightinspection "wucher/internal/domain/flight_inspection"
	"wucher/internal/shared/pkg/uuidv7"
	"wucher/internal/transport/http/dto"
	"wucher/internal/transport/http/jsonapi"
	"wucher/internal/transport/http/response"
	"wucher/internal/transport/http/validators"
)

type FlightInspectionHandler struct {
	svc      flightinspection.FlightInspectionService
	validate *validators.Validator
}

func NewFlightInspectionHandler(svc flightinspection.FlightInspectionService) *FlightInspectionHandler {
	return &FlightInspectionHandler{
		svc:      svc,
		validate: validators.New(),
	}
}

// CreateFlightInspection godoc
// @Summary Create flight inspection draft
// @Tags Flight Inspection
// @Accept json
// @Produce json
// @Param request body dto.FlightInspectionCreateRequest true "JSON:API flight inspection create request"
// @Success 201 {object} dto.FlightInspectionResponse
// @Failure 422 {object} dto.ErrorResponse
// @Failure 400 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *FlightInspectionHandler) Create(c *fiber.Ctx) error {
	var req dto.FlightInspectionCreateRequest
	if err := c.BodyParser(&req); err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Invalid JSON",
			Detail: safeInternalDetail(),
		}})
	}
	if errs := h.validate.ValidateStruct(req); errs != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, errs)
	}

	inspectionDate, err := time.Parse("2006-01-02", strings.TrimSpace(req.Data.Attributes.InspectionDate))
	if err != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
			Status: "422",
			Title:  "Validation error",
			Detail: "inspection_date must be YYYY-MM-DD",
			Source: &jsonapi.ErrorSource{Pointer: "/data/attributes/inspection_date"},
		}})
	}

	model := &flightinspection.FlightInspection{
		InspectionDate: inspectionDate.UTC(),
		CreatedBy:      actorUserID(c),
		UpdatedBy:      actorUserID(c),
	}
	if err := h.svc.CreateDraft(c.UserContext(), model); err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Create failed",
			Detail: safeInternalDetail(),
		}})
	}
	return response.Write(c, fiber.StatusCreated, flightInspectionResource(model))
}

// GetFlightInspection godoc
// @Summary Get flight inspection by ID
// @Tags Flight Inspection
// @Produce json
// @Param id path string true "Flight inspection UUID (UUIDv7)"
// @Success 200 {object} dto.FlightInspectionResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 422 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *FlightInspectionHandler) GetByID(c *fiber.Ctx) error {
	id, err := uuidv7.ParseString(strings.TrimSpace(c.Params("id")))
	if err != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
			Status: "422",
			Title:  "Validation error",
			Detail: "id is invalid UUID",
			Source: &jsonapi.ErrorSource{Pointer: "/path/id"},
		}})
	}

	row, err := h.svc.GetByID(c.UserContext(), id)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Get failed",
			Detail: safeInternalDetail(),
		}})
	}
	if row == nil {
		return response.WriteErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
			Status: "404",
			Title:  "Not found",
			Detail: "flight inspection not found",
		}})
	}
	return response.Write(c, fiber.StatusOK, flightInspectionResource(row))
}

// UpdateFlightInspection godoc
// @Summary Update flight inspection
// @Tags Flight Inspection
// @Accept json
// @Produce json
// @Param id path string true "Flight inspection UUID (UUIDv7)"
// @Param request body dto.FlightInspectionUpdateRequest true "JSON:API flight inspection update request"
// @Success 200 {object} dto.FlightInspectionResponse
// @Failure 400 {object} dto.ErrorResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 422 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *FlightInspectionHandler) Update(c *fiber.Ctx) error {
	idStr := strings.TrimSpace(c.Params("id"))
	id, err := uuidv7.ParseString(idStr)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
			Status: "422",
			Title:  "Validation error",
			Detail: "id is invalid UUID",
			Source: &jsonapi.ErrorSource{Pointer: "/path/id"},
		}})
	}

	var req dto.FlightInspectionUpdateRequest
	if err := c.BodyParser(&req); err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Invalid JSON",
			Detail: safeInternalDetail(),
		}})
	}
	if errs := h.validate.ValidateStruct(req); errs != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, errs)
	}
	if strings.TrimSpace(req.Data.ID) == "" || req.Data.ID != idStr {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
			Status: "422",
			Title:  "Validation error",
			Detail: "id is required and must match path id",
			Source: &jsonapi.ErrorSource{Pointer: "/data/id"},
		}})
	}

	if req.Data.Attributes.InspectionDate == nil && req.Data.Attributes.Status == nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
			Status: "422",
			Title:  "Validation error",
			Detail: "at least one attribute must be provided",
			Source: &jsonapi.ErrorSource{Pointer: "/data/attributes"},
		}})
	}

	row, err := h.svc.GetByID(c.UserContext(), id)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Update failed",
			Detail: safeInternalDetail(),
		}})
	}
	if row == nil {
		return response.WriteErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
			Status: "404",
			Title:  "Not found",
			Detail: "flight inspection not found",
		}})
	}

	if req.Data.Attributes.InspectionDate != nil {
		inspectionDate, parseErr := time.Parse("2006-01-02", strings.TrimSpace(*req.Data.Attributes.InspectionDate))
		if parseErr != nil {
			return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
				Status: "422",
				Title:  "Validation error",
				Detail: "inspection_date must be YYYY-MM-DD",
				Source: &jsonapi.ErrorSource{Pointer: "/data/attributes/inspection_date"},
			}})
		}
		row.InspectionDate = inspectionDate.UTC()
	}

	if req.Data.Attributes.Status != nil {
		nextStatus := strings.TrimSpace(*req.Data.Attributes.Status)
		switch nextStatus {
		case flightinspection.StatusDraft, flightinspection.StatusInProgress, flightinspection.StatusCompleted:
			row.Status = nextStatus
		default:
			return response.WriteErrors(c, fiber.StatusUnprocessableEntity, []jsonapi.ErrorObject{{
				Status: "422",
				Title:  "Validation error",
				Detail: "status must be one of: Draft, InProgress, Completed",
				Source: &jsonapi.ErrorSource{Pointer: "/data/attributes/status"},
			}})
		}
	}

	row.UpdatedBy = actorUserID(c)
	if err := h.svc.Update(c.UserContext(), row); err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "Update failed",
			Detail: safeInternalDetail(),
		}})
	}

	updated, err := h.svc.GetByID(c.UserContext(), id)
	if err == nil && updated != nil {
		return response.Write(c, fiber.StatusOK, flightInspectionResource(updated))
	}
	return response.Write(c, fiber.StatusOK, flightInspectionResource(row))
}

// ListFlightInspections godoc
// @Summary List flight inspections
// @Tags Flight Inspection
// @Produce json
// @Param page[number] query int false "Page number (default 1)"
// @Param page[size] query int false "Page size (default 20, max 100)"
// @Param sort query string false "Sort order"
// @Success 200 {object} dto.FlightInspectionListResponse
// @Failure 422 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *FlightInspectionHandler) List(c *fiber.Ctx) error {
	pageNumber := parseIntDefault(c.Query("page[number]"), 1)
	if pageNumber < 1 {
		pageNumber = 1
	}
	pageSize := parseIntDefault(c.Query("page[size]"), 20)
	if pageSize < 1 {
		pageSize = 20
	}
	if pageSize > 100 {
		pageSize = 100
	}
	offset := (pageNumber - 1) * pageSize
	_ = offset

	rows, total, err := h.svc.List(c.UserContext(), c.Query("sort"), 0, 0)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusBadRequest, []jsonapi.ErrorObject{{
			Status: "400",
			Title:  "List failed",
			Detail: safeInternalDetail(),
		}})
	}

	data := make([]dto.FlightInspectionResource, 0, len(rows))
	for i := range rows {
		data = append(data, flightInspectionResource(&rows[i]))
	}
	return response.WriteWithMeta(c, fiber.StatusOK, data, map[string]any{
		"page_number": pageNumber,
		"page_size":   pageSize,
		"total":       total,
	})
}

func flightInspectionResource(row *flightinspection.FlightInspection) dto.FlightInspectionResource {
	id, _ := uuidv7.BytesToString(row.ID)
	return dto.FlightInspectionResource{
		Type: "flight_inspection",
		ID:   id,
		Attributes: dto.FlightInspectionAttributes{
			InspectionDate: row.InspectionDate.UTC().Format("2006-01-02"),
			Status:         row.Status,
			CreatedAt:      row.CreatedAt.UTC().Format(time.RFC3339),
			CreatedBy:      uuidStringOrEmpty(row.CreatedBy),
			UpdatedAt:      row.UpdatedAt.UTC().Format(time.RFC3339),
			UpdatedBy:      uuidStringOrEmpty(row.UpdatedBy),
		},
	}
}
