package service import ( "context" "fmt" "strings" afterflightinspection "wucher/internal/domain/after_flight_inspection" "wucher/internal/shared/pkg/appctx" ) type AfterFlightInspectionService struct { repo afterflightinspection.Repository } func NewAfterFlightInspectionService(repo afterflightinspection.Repository) *AfterFlightInspectionService { return &AfterFlightInspectionService{repo: repo} } func (s *AfterFlightInspectionService) Upsert(ctx context.Context, flightInspectionID []byte, req *afterflightinspection.UpsertRequest) (*afterflightinspection.AfterFlightInspection, error) { if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil { return nil, err } caps, err := s.repo.GetHelicopterCapabilities(ctx, flightInspectionID) if err != nil { return nil, fmt.Errorf("failed to get helicopter capabilities: %w", err) } if err := s.validateRequest(req, caps); err != nil { return nil, err } row := &afterflightinspection.AfterFlightInspection{ FlightInspectionID: flightInspectionID, OilEngineNR1Checked: req.OilEngineNR1Checked, OilEngineNR2Checked: req.OilEngineNR2Checked, HydraulicLHChecked: req.HydraulicLHChecked, HydraulicRHChecked: req.HydraulicRHChecked, OilTransmissionMGBChecked: req.OilTransmissionMGBChecked, OilTransmissionIGBChecked: req.OilTransmissionIGBChecked, OilTransmissionTGBChecked: req.OilTransmissionTGBChecked, Note: req.Note, } existing, err := s.repo.GetByFlightInspectionID(ctx, flightInspectionID) if err != nil { return nil, fmt.Errorf("failed to load after flight inspection: %w", err) } userID := appctx.GetUserID(ctx) if existing != nil { row.ID = existing.ID row.CreatedAt = existing.CreatedAt row.CreatedBy = existing.CreatedBy if userID != nil { row.UpdatedBy = userID } else { row.UpdatedBy = existing.UpdatedBy } } else if userID != nil { row.CreatedBy = userID row.UpdatedBy = userID } if err := s.repo.Upsert(ctx, row); err != nil { return nil, fmt.Errorf("failed to upsert after flight inspection: %w", err) } return row, nil } func (s *AfterFlightInspectionService) GetByFlightInspectionID(ctx context.Context, flightInspectionID []byte) (*afterflightinspection.AfterFlightInspection, error) { if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil { return nil, err } row, err := s.repo.GetByFlightInspectionID(ctx, flightInspectionID) if err != nil { return nil, fmt.Errorf("failed to get after flight inspection: %w", err) } return row, nil } func (s *AfterFlightInspectionService) DeleteByFlightInspectionID(ctx context.Context, flightInspectionID []byte) error { if err := s.ensureFlightInspection(ctx, flightInspectionID); err != nil { return err } if err := s.repo.DeleteByFlightInspectionID(ctx, flightInspectionID); err != nil { return fmt.Errorf("failed to delete after flight inspection: %w", err) } return nil } // ensureFlightInspection validates the identifier and confirms the referenced // flight inspection exists. It returns domain sentinel errors so the transport // layer can map them to apperrorsx definitions. func (s *AfterFlightInspectionService) ensureFlightInspection(ctx context.Context, flightInspectionID []byte) error { if len(flightInspectionID) != 16 { return afterflightinspection.ErrInvalidFlightInspectionID } exists, err := s.repo.FlightInspectionExists(ctx, flightInspectionID) if err != nil { return fmt.Errorf("failed to check flight inspection: %w", err) } if !exists { return afterflightinspection.ErrFlightInspectionNotFound } return nil } func (s *AfterFlightInspectionService) validateRequest(req *afterflightinspection.UpsertRequest, caps *afterflightinspection.HelicopterCapabilities) error { if caps == nil { return afterflightinspection.ErrCapabilitiesUnavailable } if caps.NR1 && !hasNonEmptyString(req.OilEngineNR1Checked) { return afterflightinspection.ErrNR1Required } if hasNonEmptyString(req.OilEngineNR1Checked) && !caps.NR1 { return afterflightinspection.ErrNR1NotAvailable } if caps.NR2 && !hasNonEmptyString(req.OilEngineNR2Checked) { return afterflightinspection.ErrNR2Required } if hasNonEmptyString(req.OilEngineNR2Checked) && !caps.NR2 { return afterflightinspection.ErrNR2NotAvailable } if caps.LH && !hasNonEmptyString(req.HydraulicLHChecked) { return afterflightinspection.ErrLHRequired } if hasNonEmptyString(req.HydraulicLHChecked) && !caps.LH { return afterflightinspection.ErrLHNotAvailable } if caps.RH && !hasNonEmptyString(req.HydraulicRHChecked) { return afterflightinspection.ErrRHRequired } if hasNonEmptyString(req.HydraulicRHChecked) && !caps.RH { return afterflightinspection.ErrRHNotAvailable } if caps.MGB && !hasNonEmptyString(req.OilTransmissionMGBChecked) { return afterflightinspection.ErrMGBRequired } if hasNonEmptyString(req.OilTransmissionMGBChecked) && !caps.MGB { return afterflightinspection.ErrMGBNotAvailable } if caps.IGB && !hasNonEmptyString(req.OilTransmissionIGBChecked) { return afterflightinspection.ErrIGBRequired } if hasNonEmptyString(req.OilTransmissionIGBChecked) && !caps.IGB { return afterflightinspection.ErrIGBNotAvailable } if caps.TGB && !hasNonEmptyString(req.OilTransmissionTGBChecked) { return afterflightinspection.ErrTGBRequired } if hasNonEmptyString(req.OilTransmissionTGBChecked) && !caps.TGB { return afterflightinspection.ErrTGBNotAvailable } return nil } func hasNonEmptyString(v *string) bool { return v != nil && strings.TrimSpace(*v) != "" }