package handlers

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"

	beforeflightinspection "wucher/internal/domain/before_flight_inspection"
	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 BeforeFlightInspectionHandler struct {
	svc       beforeflightinspection.Service
	flightSvc flightinspection.FlightInspectionService
	validate  *validators.Validator
}

func NewBeforeFlightInspectionHandler(svc beforeflightinspection.Service, flightSvc flightinspection.FlightInspectionService) *BeforeFlightInspectionHandler {
	return &BeforeFlightInspectionHandler{
		svc:       svc,
		flightSvc: flightSvc,
		validate:  validators.New(),
	}
}

// Get returns before-flight inspection payload by inspection id.
func (h *BeforeFlightInspectionHandler) Get(c *fiber.Ctx) error {
	flightInspectionID, 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.GetByFlightInspectionID(c.Context(), flightInspectionID)
	if err != nil {
		if ve, ok := err.(*beforeflightinspection.ValidationError); ok {
			return response.WriteErrors(c, ve.Status, []jsonapi.ErrorObject{{
				Status: fmt.Sprintf("%d", ve.Status),
				Title:  ve.Title,
				Detail: ve.Detail,
			}})
		}
		return response.WriteErrors(c, fiber.StatusInternalServerError, []jsonapi.ErrorObject{{
			Status: "500",
			Title:  "Internal Server Error",
			Detail: safeInternalDetail(),
		}})
	}

	if row == nil {
		return response.WriteErrors(c, fiber.StatusNotFound, []jsonapi.ErrorObject{{
			Status: "404",
			Title:  "Not found",
			Detail: "before flight inspection not found",
		}})
	}

	flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusInternalServerError, []jsonapi.ErrorObject{{
			Status: "500",
			Title:  "Internal Server Error",
			Detail: safeInternalDetail(),
		}})
	}

	resp := dto.BeforeFlightInspectionResponse{
		Data: dto.BeforeFlightInspectionResource{
			Type: "before_flight_inspection",
			Attributes: dto.BeforeFlightInspectionAttributes{
				FlightInspection:          flightInspectionData,
				OilEngineNR1Checked:       row.OilEngineNR1Checked,
				OilEngineNR2Checked:       row.OilEngineNR2Checked,
				HydraulicLHChecked:        row.HydraulicLHChecked,
				HydraulicRHChecked:        row.HydraulicRHChecked,
				OilTransmissionMGBChecked: row.OilTransmissionMGBChecked,
				OilTransmissionIGBChecked: row.OilTransmissionIGBChecked,
				OilTransmissionTGBChecked: row.OilTransmissionTGBChecked,
				FuelAmount:                row.FuelAmount,
				FuelUnit:                  row.FuelUnit,
				FuelAddedAmount:           row.FuelAddedAmount,
				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)
}

// Upsert stores before-flight inspection payload by inspection id.
func (h *BeforeFlightInspectionHandler) Upsert(c *fiber.Ctx) error {
	flightInspectionID, 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"},
		}})
	}

	var req dto.BeforeFlightInspectionUpsertRequest
	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)
	}

	upsertReq := &beforeflightinspection.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,
		FuelAmount:                req.Data.Attributes.FuelAmount,
		FuelUnit:                  req.Data.Attributes.FuelUnit,
		FuelAddedAmount:           req.Data.Attributes.FuelAddedAmount,
		Note:                      req.Data.Attributes.Note,
	}

	row, err := h.svc.Upsert(c.Context(), flightInspectionID, upsertReq)
	if err != nil {
		if ve, ok := err.(*beforeflightinspection.ValidationError); ok {
			return response.WriteErrors(c, ve.Status, []jsonapi.ErrorObject{{
				Status: fmt.Sprintf("%d", ve.Status),
				Title:  ve.Title,
				Detail: ve.Detail,
			}})
		}
		return response.WriteErrors(c, fiber.StatusInternalServerError, []jsonapi.ErrorObject{{
			Status: "500",
			Title:  "Internal Server Error",
			Detail: safeInternalDetail(),
		}})
	}

	flightInspectionData, err := h.getFlightInspectionResource(c.Context(), flightInspectionID)
	if err != nil {
		return response.WriteErrors(c, fiber.StatusInternalServerError, []jsonapi.ErrorObject{{
			Status: "500",
			Title:  "Internal Server Error",
			Detail: safeInternalDetail(),
		}})
	}

	idStr, _ := uuidv7.BytesToString(row.ID)
	flightInspectionIDStr, _ := uuidv7.BytesToString(row.FlightInspectionID)
	resp := dto.BeforeFlightInspectionResponse{
		Data: dto.BeforeFlightInspectionResource{
			Type: "before_flight_inspection",
			ID:   idStr,
			Attributes: dto.BeforeFlightInspectionAttributes{
				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,
				FuelAmount:                row.FuelAmount,
				FuelUnit:                  row.FuelUnit,
				FuelAddedAmount:           row.FuelAddedAmount,
				CreatedAt:                 row.CreatedAt.Format(time.RFC3339),
				UpdatedAt:                 row.UpdatedAt.Format(time.RFC3339),
			},
		},
	}

	return c.JSON(resp)
}

func (h *BeforeFlightInspectionHandler) 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 beforeFlightInspectionResource(row), nil
}

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