package handlers

import (
	"context"
	"errors"
	"strings"
	"time"

	"github.com/gofiber/fiber/v2"

	actionsignoff "wucher/internal/domain/action_signoff"
	complaint "wucher/internal/domain/complaint"
	"wucher/internal/shared/pkg/apperrorsx"
	"wucher/internal/shared/pkg/userctx"
	"wucher/internal/shared/pkg/uuidv7"
	requestdto "wucher/internal/transport/http/dto/request"
	responsedto "wucher/internal/transport/http/dto/response"
	"wucher/internal/transport/http/jsonapi"
	"wucher/internal/transport/http/response"
)

// ActionSignoffHandler serves the corrective-action sign-off (sign/unsign) for a
// helicopter — both complaint-linked and complaint-independent.
type ActionSignoffHandler struct {
	svc          actionsignoff.Service
	complaintSvc actionSignoffComplaintLookup
}

type actionSignoffComplaintLookup interface {
	GetByID(ctx context.Context, id []byte) (*complaint.Complaint, error)
}

func NewActionSignoffHandler(svc actionsignoff.Service) *ActionSignoffHandler {
	return &ActionSignoffHandler{svc: svc}
}

// WithComplaintService wires the complaint lookup used to validate a complaint-linked
// sign-off (complaint exists and has action_taken recorded).
func (h *ActionSignoffHandler) WithComplaintService(svc actionSignoffComplaintLookup) *ActionSignoffHandler {
	h.complaintSvc = svc
	return h
}

func (h *ActionSignoffHandler) resource(ctx context.Context, row *actionsignoff.ActionSignoff) responsedto.ActionSignoffResource {
	if row == nil {
		return responsedto.ActionSignoffResource{}
	}
	id, _ := uuidv7.BytesToString(row.ID)
	out := responsedto.ActionSignoffResource{
		Type: "action_signoff",
		ID:   id,
		Attributes: responsedto.ActionSignoffAttributes{
			HelicopterID: idString(row.HelicopterID),
			FlightID:     idString(row.FlightID),
			Signed:       row.IsSigned(),
			SignedBy:     idString(row.SignedBy),
		},
	}
	if row.SignedAt != nil {
		v := row.SignedAt.UTC().Format(time.RFC3339)
		out.Attributes.SignedAt = &v
	}
	if n := userctx.GetDisplayName(ctx, row.SignedBy); n != "" {
		out.Attributes.SignedByName = &n
	}
	if s := userctx.GetShortName(ctx, row.SignedBy); s != "" {
		out.Attributes.SignedByShort = &s
	}
	if !row.CreatedAt.IsZero() {
		out.Attributes.CreatedAt = row.CreatedAt.UTC().Format(time.RFC3339)
	}
	if !row.UpdatedAt.IsZero() {
		out.Attributes.UpdatedAt = row.UpdatedAt.UTC().Format(time.RFC3339)
	}
	return out
}

func actionSignoffHelicopterID(c *fiber.Ctx) ([]byte, *apperrorsx.AppError) {
	id, err := uuidv7.ParseString(strings.TrimSpace(c.Params("helicopter_id")))
	if err != nil {
		appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
		appErr.Source = &jsonapi.ErrorSource{Pointer: "/path/helicopter_id"}
		return nil, appErr
	}
	return id, nil
}

func actionSignoffFlightID(c *fiber.Ctx) ([]byte, *apperrorsx.AppError) {
	id, err := uuidv7.ParseString(strings.TrimSpace(c.Params("flight_id")))
	if err != nil {
		appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
		appErr.Source = &jsonapi.ErrorSource{Pointer: "/path/flight_id"}
		return nil, appErr
	}
	return id, nil
}

// actionSignoffBodyFlightID parses the REQUIRED flight_id from the sign body — the
// flight the sign-off is attributed to (standalone) or performed in (complaint-linked).
func actionSignoffBodyFlightID(req requestdto.ActionSignoffSignRequest) ([]byte, *apperrorsx.AppError) {
	raw := ""
	if req.Data.Attributes.FlightID != nil {
		raw = strings.TrimSpace(*req.Data.Attributes.FlightID)
	}
	if raw == "" {
		appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID)
		appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/flight_id"}
		return nil, appErr
	}
	id, err := uuidv7.ParseString(raw)
	if err != nil {
		appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(err)
		appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/flight_id"}
		return nil, appErr
	}
	return id, nil
}

// GetByHelicopter godoc
// @Summary Get the current corrective-action sign-off for a helicopter
// @Description Returns the fleet-level (complaint-independent) corrective-action sign-off state for a helicopter — `signed`, `signed_at`, `signed_by`. When none exists yet it returns an unsigned placeholder. A signed sign-off is what gates the EASA maintenance release.
// @Tags Complaints - Sign Off
// @Produce json
// @Param flight_id path string true "Flight ID (UUIDv7)"
// @Success 200 {object} responsedto.ActionSignoffResponse
// @Router /api/v1/action-signoffs/get-by-flight/{flight_id} [get]
func (h *ActionSignoffHandler) GetByFlight(c *fiber.Ctx) error {
	flightID, aerr := actionSignoffFlightID(c)
	if aerr != nil {
		return apperrorsx.Write(c, aerr)
	}
	row, err := h.svc.GetByFlight(c.UserContext(), flightID)
	if err != nil {
		return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffNotFound).WithCause(err))
	}
	if row == nil {
		// No row yet: return an unsigned placeholder so the client can render.
		return response.Write(c, fiber.StatusOK, responsedto.ActionSignoffResource{
			Type: "action_signoff",
			Attributes: responsedto.ActionSignoffAttributes{
				FlightID: idString(flightID),
				Signed:   false,
			},
		})
	}
	return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
}

// Sign godoc
// @Summary Toggle the corrective-action sign-off (per complaint or fleet-wide)
// @Description Toggles the corrective-action sign-off for a helicopter. `sign` (optional, default true) chooses the direction: true signs, false unsigns. Send `complaint_id` to target a specific complaint's sign-off — signing it requires `action_taken` recorded (else 422 `ACTION_SIGNOFF_ACTION_REQUIRED`), and 404 `ACTION_SIGNOFF_COMPLAINT_NOT_FOUND` if the complaint does not exist. Omit `complaint_id` for a fleet-wide (complaint-independent) sign-off. A signed sign-off is what allows an EASA maintenance release to be created for the helicopter.
// @Tags Complaints - Sign Off
// @Accept json
// @Produce json
// @Param helicopter_id path string true "Helicopter ID (UUIDv7)"
// @Param request body requestdto.ActionSignoffSignRequest false "Optional complaint_id + note"
// @Success 200 {object} responsedto.ActionSignoffResponse
// @Failure 404 {object} jsonapi.ErrorDocument
// @Failure 422 {object} jsonapi.ErrorDocument
// @Router /api/v1/action-signoffs/sign/{helicopter_id} [post]
func (h *ActionSignoffHandler) Sign(c *fiber.Ctx) error {
	helicopterID, err := actionSignoffHelicopterID(c)
	if err != nil {
		return apperrorsx.Write(c, err)
	}
	// Body is optional — the sign-off can be checked with nothing filled.
	var req requestdto.ActionSignoffSignRequest
	_ = c.BodyParser(&req)
	actor := actorUserID(c)
	// sign toggles the state: true (or omitted) signs, false unsigns.
	signWanted := req.Data.Attributes.Sign == nil || *req.Data.Attributes.Sign

	complaintIDRaw := ""
	if req.Data.Attributes.ComplaintID != nil {
		complaintIDRaw = strings.TrimSpace(*req.Data.Attributes.ComplaintID)
	}

	// Complaint-linked toggle: sign/unsign that complaint's corrective action.
	if complaintIDRaw != "" {
		complaintID, perr := uuidv7.ParseString(complaintIDRaw)
		if perr != nil {
			appErr := apperrorsx.New(apperrorsx.ErrActionSignoffInvalidUUID).WithCause(perr)
			appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/complaint_id"}
			return apperrorsx.Write(c, appErr)
		}
		if !signWanted {
			row, serr := h.svc.UnsignByComplaint(c.UserContext(), complaintID)
			if serr != nil {
				return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffUnsignFailed).WithCause(serr))
			}
			if row == nil {
				return response.Write(c, fiber.StatusOK, unsignedActionSignoffPlaceholder(helicopterID))
			}
			return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
		}
		if h.complaintSvc == nil {
			return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed))
		}
		comp, cerr := h.complaintSvc.GetByID(c.UserContext(), complaintID)
		if cerr != nil {
			return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(cerr))
		}
		if comp == nil {
			return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffComplaintNotFound))
		}
		// The complaint's corrective action must be recorded before it can be signed off.
		if comp.ActionTaken == nil || strings.TrimSpace(*comp.ActionTaken) == "" {
			appErr := apperrorsx.New(apperrorsx.ErrActionSignoffActionRequired)
			appErr.Source = &jsonapi.ErrorSource{Pointer: "/data/attributes/complaint_id"}
			return apperrorsx.Write(c, appErr)
		}
		flightID, ferr := actionSignoffBodyFlightID(req)
		if ferr != nil {
			return apperrorsx.Write(c, ferr)
		}
		row, serr := h.svc.SignForComplaint(c.UserContext(), complaintID, flightID, comp.HelicopterID, actor)
		if serr != nil {
			return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(serr))
		}
		return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
	}

	// Complaint-independent (fleet) toggle — scoped to a flight.
	flightID, ferr := actionSignoffBodyFlightID(req)
	if ferr != nil {
		return apperrorsx.Write(c, ferr)
	}
	if !signWanted {
		row, serr := h.svc.Unsign(c.UserContext(), flightID, actor)
		if errors.Is(serr, actionsignoff.ErrNotFound) {
			return response.Write(c, fiber.StatusOK, unsignedActionSignoffPlaceholder(helicopterID))
		}
		if serr != nil {
			return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffUnsignFailed).WithCause(serr))
		}
		return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
	}
	// Signing a fleet sign-off — this is what allows an EASA release for the flight.
	row, serr := h.svc.Sign(c.UserContext(), flightID, helicopterID, actor)
	if serr != nil {
		return apperrorsx.Write(c, apperrorsx.New(apperrorsx.ErrActionSignoffSignFailed).WithCause(serr))
	}
	return response.Write(c, fiber.StatusOK, h.resource(c.UserContext(), row))
}

// unsignedActionSignoffPlaceholder renders an unsigned resource for a helicopter that
// has no sign-off row (e.g. unsigning something that was never signed).
func unsignedActionSignoffPlaceholder(helicopterID []byte) responsedto.ActionSignoffResource {
	return responsedto.ActionSignoffResource{
		Type: "action_signoff",
		Attributes: responsedto.ActionSignoffAttributes{
			HelicopterID: idString(helicopterID),
			Signed:       false,
		},
	}
}
