package handlers

import (
	"context"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"

	afterflightinspection "wucher/internal/domain/after_flight_inspection"
	flightinspection "wucher/internal/domain/flight_inspection"
	"wucher/internal/shared/pkg/apperrorsx"
	"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 AfterFlightInspectionHandler struct {
	svc       afterflightinspection.Service
	flightSvc flightinspection.FlightInspectionService
	validate  *validators.Validator
}

func NewAfterFlightInspectionHandler(svc afterflightinspection.Service, flightSvc flightinspection.FlightInspectionService) *AfterFlightInspectionHandler {
	return &AfterFlightInspectionHandler{
		svc:       svc,
		flightSvc: flightSvc,
		validate:  validators.New(),
	}
}

// GetAfterFlightInspection godoc
// @Summary Get after flight inspection by flight inspection ID
// @Tags Flights - After Flight Inspection
// @Accept json
// @Produce json
// @Param id path string true "Flight Inspection ID"
// @Success 200 {object} dto.AfterFlightInspectionResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 400 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *AfterFlightInspectionHandler) Get(c *fiber.Ctx) error {
	flightInspectionID, err := uuidv7.ParseString(strings.TrimSpace(c.Params("id")))
	if err != nil {
		return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidID, &jsonapi.ErrorSource{Pointer: "/path/id"})
	}

	row, err := h.svc.GetByFlightInspectionID(c.Context(), flightInspectionID)
	if err != nil {
		return writeAfterFlightServiceError(c, err)
	}

	if row == nil {
		return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInspectionNotFound, nil)
	}

	flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
	if err != nil {
		return writeAfterFlightServiceError(c, err)
	}

	resp := dto.AfterFlightInspectionResponse{
		Data: dto.AfterFlightInspectionResource{
			Type: "after_flight_inspection",
			Attributes: dto.AfterFlightInspectionAttributes{
				FlightInspection:          flightInspectionData,
				OilEngineNR1Checked:       row.OilEngineNR1Checked,
				OilEngineNR2Checked:       row.OilEngineNR2Checked,
				HydraulicLHChecked:        row.HydraulicLHChecked,
				HydraulicRHChecked:        row.HydraulicRHChecked,
				OilTransmissionMGBChecked: row.OilTransmissionMGBChecked,
				OilTransmissionIGBChecked: row.OilTransmissionIGBChecked,
				OilTransmissionTGBChecked: row.OilTransmissionTGBChecked,
				Note:                      row.Note,
				CreatedAt:                 row.CreatedAt.Format(time.RFC3339),
				UpdatedAt:                 row.UpdatedAt.Format(time.RFC3339),
			},
		},
	}

	idStr, _ := uuidv7.BytesToString(row.ID)
	flightInspectionIDStr, _ := uuidv7.BytesToString(row.FlightInspectionID)
	resp.Data.ID = idStr
	resp.Data.Attributes.ID = idStr
	resp.Data.Attributes.FlightInspectionID = flightInspectionIDStr

	return c.JSON(resp)
}

// UpsertAfterFlightInspection godoc
// @Summary Upsert after flight inspection by flight inspection ID
// @Tags Flights - After Flight Inspection
// @Accept json
// @Produce json
// @Param id path string true "Flight Inspection ID"
// @Param request body dto.AfterFlightInspectionUpsertRequest true "JSON:API after flight inspection upsert request"
// @Success 200 {object} dto.AfterFlightInspectionResponse
// @Failure 422 {object} dto.ErrorResponse
// @Failure 404 {object} dto.ErrorResponse
// @Failure 400 {object} dto.ErrorResponse
// Public route disabled (managed via reserve AC flow).
func (h *AfterFlightInspectionHandler) Upsert(c *fiber.Ctx) error {
	flightInspectionID, err := uuidv7.ParseString(strings.TrimSpace(c.Params("id")))
	if err != nil {
		return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidID, &jsonapi.ErrorSource{Pointer: "/path/id"})
	}

	var req dto.AfterFlightInspectionUpsertRequest
	if err := c.BodyParser(&req); err != nil {
		return writeAfterFlightAppError(c, apperrorsx.ErrAfterFlightInvalidJSON, nil)
	}
	if errs := h.validate.ValidateStruct(req); errs != nil {
		return response.WriteErrors(c, fiber.StatusUnprocessableEntity, errs)
	}

	upsertReq := &afterflightinspection.UpsertRequest{
		OilEngineNR1Checked:       req.Data.Attributes.OilEngineNR1Checked,
		OilEngineNR2Checked:       req.Data.Attributes.OilEngineNR2Checked,
		HydraulicLHChecked:        req.Data.Attributes.HydraulicLHChecked,
		HydraulicRHChecked:        req.Data.Attributes.HydraulicRHChecked,
		OilTransmissionMGBChecked: req.Data.Attributes.OilTransmissionMGBChecked,
		OilTransmissionIGBChecked: req.Data.Attributes.OilTransmissionIGBChecked,
		OilTransmissionTGBChecked: req.Data.Attributes.OilTransmissionTGBChecked,
		Note:                      req.Data.Attributes.Note,
	}

	row, err := h.svc.Upsert(c.Context(), flightInspectionID, upsertReq)
	if err != nil {
		return writeAfterFlightServiceError(c, err)
	}

	flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
	if err != nil {
		return writeAfterFlightServiceError(c, err)
	}

	idStr, _ := uuidv7.BytesToString(row.ID)
	flightInspectionIDStr, _ := uuidv7.BytesToString(row.FlightInspectionID)
	resp := dto.AfterFlightInspectionResponse{
		Data: dto.AfterFlightInspectionResource{
			Type: "after_flight_inspection",
			ID:   idStr,
			Attributes: dto.AfterFlightInspectionAttributes{
				ID:                        idStr,
				FlightInspectionID:        flightInspectionIDStr,
				FlightInspection:          flightInspectionData,
				OilEngineNR1Checked:       row.OilEngineNR1Checked,
				OilEngineNR2Checked:       row.OilEngineNR2Checked,
				HydraulicLHChecked:        row.HydraulicLHChecked,
				HydraulicRHChecked:        row.HydraulicRHChecked,
				OilTransmissionMGBChecked: row.OilTransmissionMGBChecked,
				OilTransmissionIGBChecked: row.OilTransmissionIGBChecked,
				OilTransmissionTGBChecked: row.OilTransmissionTGBChecked,
				Note:                      row.Note,
				CreatedAt:                 row.CreatedAt.Format(time.RFC3339),
				UpdatedAt:                 row.UpdatedAt.Format(time.RFC3339),
			},
		},
	}

	return c.JSON(resp)
}

func (h *AfterFlightInspectionHandler) getFlightInspectionResource(ctx context.Context, flightInspectionID []byte) (*dto.FlightInspectionResource, error) {
	if h.flightSvc == nil {
		return nil, nil
	}

	row, err := h.flightSvc.GetByID(ctx, flightInspectionID)
	if err != nil {
		return nil, err
	}
	if row == nil {
		return nil, nil
	}

	return afterFlightInspectionResource(row), nil
}

// writeAfterFlightAppError writes a known apperrorsx definition, optionally
// attaching a JSON:API source pointer.
func writeAfterFlightAppError(c *fiber.Ctx, def apperrorsx.Def, source *jsonapi.ErrorSource) error {
	appErr := apperrorsx.New(def)
	if source != nil {
		appErr.Source = source
	}
	return apperrorsx.Write(c, appErr)
}

// writeAfterFlightServiceError translates a domain/service error into its
// apperrorsx definition; unknown errors fall back to a generic internal error.
func writeAfterFlightServiceError(c *fiber.Ctx, err error) error {
	if mapped := apperrorsx.Translate(apperrorsx.ModuleAfterFlightInspection, err); mapped != nil && mapped.Code != apperrorsx.CodeInternalServerError {
		return apperrorsx.Write(c, mapped)
	}
	return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrInternal))
}

func afterFlightInspectionResource(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),
		},
	}
}
