package dto

import (
	"context"
	"strings"
	"time"

	"wucher/internal/domain/base"
	"wucher/internal/shared/pkg/userctx"
	"wucher/internal/shared/pkg/uuidv7"
)

// JSON:API Base DTOs

type BaseAttributes struct {
	BaseName                           string                     `json:"base" example:"Lude"`
	BaseAbbreviation                   string                     `json:"base_abbreviation,omitempty" example:"LOIG"`
	BaseCategory                       string                     `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
	Address                            string                     `json:"address,omitempty" example:"Lude Airfield"`
	Latitude                           float64                    `json:"latitude" example:"47.3769"`
	Longitude                          float64                    `json:"longitude" example:"8.5417"`
	LandlineNumber                     string                     `json:"landline_number,omitempty" example:"+62-21-1234"`
	MobileNumber                       string                     `json:"mobile_number,omitempty" example:"+62-812-1234"`
	Email                              string                     `json:"email,omitempty" example:"lude@wucher.local"`
	SortKey                            *int                       `json:"sortkey" example:"1"`
	SMSAlert                           bool                       `json:"sms_alert" example:"false"`
	Checklist                          bool                       `json:"checklist" example:"false"`
	LegTime                            string                     `json:"leg_time,omitempty" example:"00:20"`
	DefaultStartTimeType               string                     `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	DefaultEndTimeType                 string                     `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	ShiftTime                          BaseShiftTime              `json:"shift_time"`
	UTC                                string                     `json:"utc" example:"0"`
	Foto                               *BaseFoto                  `json:"foto,omitempty"`
	Dry                                bool                       `json:"dry" example:"false"`
	ControlCenter                      bool                       `json:"control_center" example:"false"`
	DUL                                bool                       `json:"dul" example:"false"`
	Notes                              string                     `json:"notes,omitempty" example:"Main base"`
	HEMSEDCContactIDs []string      `json:"hems_edc_contact_ids,omitempty"`
	HEMSEDCs          []BaseContact `json:"hems_edc,omitempty"`
	MedPaxContactIDs  []string      `json:"med_pax_contact_ids,omitempty"`
	MedPax            []BaseContact `json:"med_pax,omitempty"`
	ResponsiblePilotContactIDs         []string                   `json:"responsible_pilot_contact_ids,omitempty"`
	ResponsiblePilots                  []BaseContact              `json:"responsible_pilots,omitempty"`
	OperationalShiftTimes              []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
	IsActive                           bool                       `json:"is_active" example:"true"`
	CreatedBy                          string                     `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
	UpdatedBy                          string                     `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
	CreatedAt                          string                     `json:"created_at,omitempty" example:"2026-03-12T03:04:05Z"`
	UpdatedAt                          string                     `json:"updated_at,omitempty" example:"2026-03-12T03:04:05Z"`
	DeletedAt                          string                     `json:"deleted_at,omitempty" example:"2026-03-12T03:04:05Z" swaggerignore:"true"`
	DeletedBy                          string                     `json:"deleted_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
}

type BaseContact struct {
	ID        string `json:"id"`
	FirstName string `json:"first_name,omitempty"`
	LastName  string `json:"last_name,omitempty"`
}

type BaseShiftTime struct {
	Start string `json:"start,omitempty" example:"06:00"`
	End   string `json:"end,omitempty" example:"21:00"`
}

type BaseOperationalShiftTime struct {
	DateStart     string        `json:"date_start" example:"2026-04-12"`
	DateEnd       string        `json:"date_end" example:"2026-04-12"`
	StartTimeType string        `json:"start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	EndTimeType   string        `json:"end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	ShiftTime     BaseShiftTime `json:"shift_time"`
}

type BaseFoto struct {
	UUID                 string `json:"uuid" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
	DownloadURL          string `json:"download_url" example:"https://storage.example.com/base/lude.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
	ThumbnailDownloadURL string `json:"thumbnail_download_url,omitempty" example:"https://storage.example.com/base/lude.thumb.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=900&X-Amz-Signature=abc123"`
	OriginalSizeBytes    *int64 `json:"original_size_bytes,omitempty" example:"1024000"`
}

type BaseResource struct {
	Type       string         `json:"type" example:"base"`
	ID         string         `json:"id" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
	Attributes BaseAttributes `json:"attributes"`
}

type BaseResponse struct {
	Data BaseResource `json:"data"`
}

type BaseListResponse struct {
	Data []BaseResource `json:"data"`
	Meta struct {
		PageNumber int   `json:"page_number" example:"1"`
		PageSize   int   `json:"page_size" example:"20"`
		Total      int64 `json:"total" example:"120"`
	} `json:"meta"`
}

type BaseDataTableResponse struct {
	Data []BaseResource `json:"data"`
	Meta struct {
		Draw            int   `json:"draw" example:"1"`
		RecordsTotal    int64 `json:"records_total" example:"120"`
		RecordsFiltered int64 `json:"records_filtered" example:"12"`
	} `json:"meta"`
}

type BaseCreateAttributes struct {
	BaseName                           string                     `json:"base" validate:"required" example:"Lude"`
	BaseAbbreviation                   *string                    `json:"base_abbreviation,omitempty" example:"LOIG"`
	BaseCategory                       *string                    `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
	Address                            *string                    `json:"address,omitempty" example:"Lude Airfield"`
	Latitude                           *float64                   `json:"latitude" validate:"required,gte=-90,lte=90" example:"47.3769"`
	Longitude                          *float64                   `json:"longitude" validate:"required,gte=-180,lte=180" example:"8.5417"`
	LandlineNumber                     *string                    `json:"landline_number,omitempty" example:"+62-21-1234"`
	MobileNumber                       *string                    `json:"mobile_number,omitempty" example:"+62-812-1234"`
	Email                              *string                    `json:"email,omitempty" example:"lude@wucher.local"`
	SortKey                            *int                       `json:"sortkey" example:"1"`
	SMSAlert                           *bool                      `json:"sms_alert,omitempty" example:"false"`
	Checklist                          *bool                      `json:"checklist,omitempty" example:"false"`
	LegTime                            *string                    `json:"leg_time,omitempty" example:"00:20"`
	DefaultStartTimeType               *string                    `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	DefaultEndTimeType                 *string                    `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	ShiftTime                          *BaseShiftTime             `json:"shift_time,omitempty"`
	DefaultShiftStart                  *string                    `json:"default_shift_start,omitempty" example:"06:00:00"`
	DefaultShiftEnd                    *string                    `json:"default_shift_end,omitempty" example:"21:00:00"`
	DefaultShiftTime                   *string                    `json:"default_shift_time,omitempty" example:"12:00"`
	UTC                                *string                    `json:"utc,omitempty" example:"0"`
	FileUUID                           *string                    `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
	Dry                                *bool                      `json:"dry,omitempty" example:"false"`
	ControlCenter                      *bool                      `json:"control_center,omitempty" example:"false"`
	DUL                                *bool                      `json:"dul,omitempty" example:"false"`
	Notes                              *string                    `json:"notes,omitempty" example:"Main base"`
	HEMSEDCContactIDs []string `json:"hems_edc_contact_ids,omitempty"`
	MedPaxContactIDs  []string `json:"med_pax_contact_ids,omitempty"`
	ResponsiblePilotContactIDs         []string                   `json:"responsible_pilot_contact_ids,omitempty"`
	OperationalShiftTimes              []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
	IsActive                           *bool                      `json:"is_active,omitempty" example:"true"`
	CreatedBy                          *string                    `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
	UpdatedBy                          *string                    `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
}

type BaseCreateData struct {
	Type       string               `json:"type" validate:"required,oneof=base_create base hems_base" enums:"base" example:"base"`
	Attributes BaseCreateAttributes `json:"attributes" validate:"required"`
}

type BaseCreateRequest struct {
	Data BaseCreateData `json:"data" validate:"required"`
}

type BaseUpdateAttributes struct {
	BaseName                           *string                    `json:"base,omitempty" example:"Lude"`
	BaseAbbreviation                   *string                    `json:"base_abbreviation,omitempty" example:"LOIG"`
	BaseCategory                       *string                    `json:"base_category,omitempty" example:"regular" enums:"regular,hems"`
	Address                            *string                    `json:"address,omitempty" example:"Lude Airfield"`
	Latitude                           *float64                   `json:"latitude,omitempty" validate:"omitempty,gte=-90,lte=90" example:"47.3769"`
	Longitude                          *float64                   `json:"longitude,omitempty" validate:"omitempty,gte=-180,lte=180" example:"8.5417"`
	LandlineNumber                     *string                    `json:"landline_number,omitempty" example:"+62-21-1234"`
	MobileNumber                       *string                    `json:"mobile_number,omitempty" example:"+62-812-1234"`
	Email                              *string                    `json:"email,omitempty" example:"lude@wucher.local"`
	SortKey                            NullInt                    `json:"sortkey,omitempty" swaggertype:"integer" example:"1"`
	SMSAlert                           *bool                      `json:"sms_alert,omitempty" example:"false"`
	Checklist                          *bool                      `json:"checklist,omitempty" example:"false"`
	LegTime                            *string                    `json:"leg_time,omitempty" example:"00:20"`
	DefaultStartTimeType               *string                    `json:"default_start_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	DefaultEndTimeType                 *string                    `json:"default_end_time_type,omitempty" example:"FIXED" enums:"FIXED,BMCT,ECET"`
	DefaultShiftStart                  *string                    `json:"default_shift_start,omitempty" example:"06:00:00"`
	DefaultShiftEnd                    *string                    `json:"default_shift_end,omitempty" example:"21:00:00"`
	DefaultShiftTime                   *string                    `json:"default_shift_time,omitempty" example:"12:00"`
	ShiftTime                          *BaseShiftTime             `json:"shift_time,omitempty"`
	UTC                                *string                    `json:"utc,omitempty" example:"0"`
	FileUUID                           *string                    `json:"file_uuid,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e90"`
	Dry                                *bool                      `json:"dry,omitempty" example:"false"`
	ControlCenter                      *bool                      `json:"control_center,omitempty" example:"false"`
	DUL                                *bool                      `json:"dul,omitempty" example:"false"`
	Notes                              *string                    `json:"notes,omitempty" example:"Main base"`
	HEMSEDCContactIDs *[]string `json:"hems_edc_contact_ids,omitempty"`
	MedPaxContactIDs  *[]string `json:"med_pax_contact_ids,omitempty"`
	ResponsiblePilotContactIDs         *[]string                  `json:"responsible_pilot_contact_ids,omitempty"`
	OperationalShiftTimes              []BaseOperationalShiftTime `json:"operational_shift_times,omitempty"`
	IsActive                           *bool                      `json:"is_active,omitempty" example:"true"`
	CreatedBy                          *string                    `json:"created_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
	UpdatedBy                          *string                    `json:"updated_by,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d" swaggerignore:"true"`
}

type BaseUpdateData struct {
	Type       string               `json:"type" validate:"required,oneof=base_update base hems_base" enums:"base_update" example:"base_update"`
	ID         string               `json:"id,omitempty" example:"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"`
	Attributes BaseUpdateAttributes `json:"attributes" validate:"required"`
}

type BaseUpdateRequest struct {
	Data BaseUpdateData `json:"data" validate:"required"`
}

type BaseDeleteAttributes struct {
	Deleted bool `json:"deleted" example:"true"`
}

type BaseDeleteResource struct {
	Type       string               `json:"type" example:"base_delete"`
	ID         string               `json:"id" example:"019d6b10-a385-7e9e-a553-8e892bcec0ef"`
	Attributes BaseDeleteAttributes `json:"attributes"`
}

type BaseDeleteResponse struct {
	Data BaseDeleteResource `json:"data"`
}

type BaseResponseMappingInput struct {
	ResourceType                       string
	BaseCategory                       string
	ID                                 string
	DefaultStartTimeType               string
	DefaultEndTimeType                 string
	ShiftTime                          BaseShiftTime
	Foto                               *BaseFoto
	Latitude                           float64
	Longitude                          float64
	HEMSEDCContactIDs []string
	HEMSEDCs          []BaseContact
	MedPaxContactIDs  []string
	MedPax            []BaseContact
	ResponsiblePilotContactIDs         []string
	ResponsiblePilots                  []BaseContact
	OperationalShiftTimes              []BaseOperationalShiftTime
}

func MapBaseCreateAttributesToDomain(attrs BaseCreateAttributes) *base.Base {
	row := &base.Base{}
	row.BaseName = strings.TrimSpace(attrs.BaseName)
	row.BaseAbbreviation = strings.TrimSpace(stringOrDefault(attrs.BaseAbbreviation, ""))
	row.Address = strings.TrimSpace(stringOrDefault(attrs.Address, ""))
	row.Latitude = floatOrDefault(attrs.Latitude, 0)
	row.Longitude = floatOrDefault(attrs.Longitude, 0)
	row.LandlineNumber = strings.TrimSpace(stringOrDefault(attrs.LandlineNumber, ""))
	row.MobileNumber = strings.TrimSpace(stringOrDefault(attrs.MobileNumber, ""))
	row.Email = strings.TrimSpace(stringOrDefault(attrs.Email, ""))
	row.SortKey = cloneIntPtr(attrs.SortKey)
	row.SMSAlert = boolOrDefault(attrs.SMSAlert, false)
	row.Checklist = boolOrDefault(attrs.Checklist, false)
	row.LegTime = strings.TrimSpace(stringOrDefault(attrs.LegTime, ""))
	row.DefaultStartTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultStartTimeType, ""))
	row.DefaultEndTimeType = normalizeShiftTimeType(stringOrDefault(attrs.DefaultEndTimeType, ""))
	row.UTC = normalizeUTC(attrs.UTC)
	row.Dry = boolOrDefault(attrs.Dry, false)
	row.ControlCenter = boolOrDefault(attrs.ControlCenter, false)
	row.DUL = boolOrDefault(attrs.DUL, false)
	row.Notes = strings.TrimSpace(stringOrDefault(attrs.Notes, ""))
	row.IsActive = boolOrDefault(attrs.IsActive, true)
	row.OperationalShiftTimes = mapBaseOperationalShiftTimesToDomain(attrs.OperationalShiftTimes)
	return row
}

func ApplyBaseUpdateAttributesToDomain(row *base.Base, attrs BaseUpdateAttributes) {
	if attrs.BaseName != nil {
		row.BaseName = strings.TrimSpace(*attrs.BaseName)
	}
	if attrs.BaseAbbreviation != nil {
		row.BaseAbbreviation = strings.TrimSpace(*attrs.BaseAbbreviation)
	}
	if attrs.Address != nil {
		row.Address = strings.TrimSpace(*attrs.Address)
	}
	if attrs.Latitude != nil {
		row.Latitude = *attrs.Latitude
	}
	if attrs.Longitude != nil {
		row.Longitude = *attrs.Longitude
	}
	if attrs.LandlineNumber != nil {
		row.LandlineNumber = strings.TrimSpace(*attrs.LandlineNumber)
	}
	if attrs.MobileNumber != nil {
		row.MobileNumber = strings.TrimSpace(*attrs.MobileNumber)
	}
	if attrs.Email != nil {
		row.Email = strings.TrimSpace(*attrs.Email)
	}
	if attrs.SortKey.Set {
		if attrs.SortKey.Valid {
			v := attrs.SortKey.Value
			row.SortKey = &v
		} else {
			row.SortKey = nil
		}
	}
	if attrs.SMSAlert != nil {
		row.SMSAlert = *attrs.SMSAlert
	}
	if attrs.Checklist != nil {
		row.Checklist = *attrs.Checklist
	}
	if attrs.LegTime != nil {
		row.LegTime = strings.TrimSpace(*attrs.LegTime)
	}
	if attrs.DefaultStartTimeType != nil {
		row.DefaultStartTimeType = normalizeShiftTimeType(*attrs.DefaultStartTimeType)
	}
	if attrs.DefaultEndTimeType != nil {
		row.DefaultEndTimeType = normalizeShiftTimeType(*attrs.DefaultEndTimeType)
	}
	if attrs.UTC != nil {
		row.UTC = normalizeUTC(attrs.UTC)
	}
	if attrs.Dry != nil {
		row.Dry = *attrs.Dry
	}
	if attrs.ControlCenter != nil {
		row.ControlCenter = *attrs.ControlCenter
	}
	if attrs.DUL != nil {
		row.DUL = *attrs.DUL
	}
	if attrs.Notes != nil {
		row.Notes = strings.TrimSpace(*attrs.Notes)
	}
	if attrs.IsActive != nil {
		row.IsActive = *attrs.IsActive
	}
}

func mapBaseOperationalShiftTimesToDomain(items []BaseOperationalShiftTime) []base.BaseOperationalShiftTime {
	if len(items) == 0 {
		return nil
	}
	out := make([]base.BaseOperationalShiftTime, 0, len(items))
	for i := range items {
		dateStart := parseBaseOperationDatePtr(items[i].DateStart)
		dateEnd := parseBaseOperationDatePtr(items[i].DateEnd)
		out = append(out, base.BaseOperationalShiftTime{
			DateStart:     dateStart,
			DateEnd:       dateEnd,
			StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
			EndTimeType:   normalizeShiftTimeType(items[i].EndTimeType),
			ShiftStart:    normalizeBaseClockValue(items[i].ShiftTime.Start),
			ShiftEnd:      normalizeBaseClockValue(items[i].ShiftTime.End),
		})
	}
	return out
}

func MapBaseOperationalShiftTimesToDTO(items []base.BaseOperationalShiftTime) []BaseOperationalShiftTime {
	if len(items) == 0 {
		return nil
	}
	out := make([]BaseOperationalShiftTime, 0, len(items))
	for i := range items {
		out = append(out, BaseOperationalShiftTime{
			DateStart:     timePtrToDateString(items[i].DateStart),
			DateEnd:       timePtrToDateString(items[i].DateEnd),
			StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
			EndTimeType:   normalizeShiftTimeType(items[i].EndTimeType),
			ShiftTime: BaseShiftTime{
				Start: DisplayBaseShiftValue(items[i].StartTimeType, items[i].ShiftStart),
				End:   DisplayBaseShiftValue(items[i].EndTimeType, items[i].ShiftEnd),
			},
		})
	}
	return out
}

func parseBaseOperationDate(raw string) time.Time {
	parsed, err := time.Parse("2006-01-02", strings.TrimSpace(raw))
	if err != nil {
		return time.Time{}
	}
	return parsed.UTC()
}

func parseBaseOperationDatePtr(raw string) *time.Time {
	parsed := parseBaseOperationDate(raw)
	if parsed.IsZero() {
		return nil
	}
	return &parsed
}

func timePtrToDateString(v *time.Time) string {
	if v == nil || v.IsZero() {
		return ""
	}
	return v.UTC().Format("2006-01-02")
}

func normalizeBaseClockValue(raw string) string {
	trimmed := strings.TrimSpace(raw)
	if trimmed == "" {
		return ""
	}
	if len(trimmed) == 5 {
		trimmed += ":00"
	}
	parsed, err := time.Parse("15:04:05", trimmed)
	if err != nil {
		return ""
	}
	return parsed.UTC().Format("15:04:05")
}

func normalizeBaseShiftDisplay(raw string) string {
	s := strings.TrimSpace(raw)
	if s == "" {
		return s
	}
	layouts := []string{
		"15:04:05",
		"15:04",
		"2006-01-02 15:04:05",
		"2006-01-02 15:04",
		time.RFC3339,
		time.RFC3339Nano,
	}
	for _, layout := range layouts {
		if parsed, err := time.Parse(layout, s); err == nil {
			return parsed.UTC().Format("15:04")
		}
	}
	if len(s) >= 5 && isDigitBaseClock(s[0]) && isDigitBaseClock(s[1]) && s[2] == ':' && isDigitBaseClock(s[3]) && isDigitBaseClock(s[4]) {
		return s[:5]
	}
	return s
}

// DisplayBaseShiftValue returns the user-facing representation for a shift value.
// Fixed shifts are shown as HH:MM, while BMCT/ECET are shown as labels.
func DisplayBaseShiftValue(timeType, raw string) string {
	switch normalizeShiftTimeType(timeType) {
	case "BMCT", "ECET":
		return normalizeShiftTimeType(timeType)
	default:
		return normalizeBaseShiftDisplay(raw)
	}
}

func normalizeShiftTimeType(raw string) string {
	switch strings.ToUpper(strings.TrimSpace(raw)) {
	case "", "FIXED":
		return "FIXED"
	case "BMCT":
		return "BMCT"
	case "ECET":
		return "ECET"
	default:
		return strings.ToUpper(strings.TrimSpace(raw))
	}
}

func isDigitBaseClock(b byte) bool {
	return b >= '0' && b <= '9'
}

func MapBaseToResource(row *base.Base, in BaseResponseMappingInput) BaseResource {
	if row == nil {
		return BaseResource{
			Type: in.ResourceType,
			Attributes: BaseAttributes{
				BaseCategory:          in.BaseCategory,
				DefaultStartTimeType:  normalizeShiftTimeType(in.DefaultStartTimeType),
				DefaultEndTimeType:    normalizeShiftTimeType(in.DefaultEndTimeType),
				ShiftTime:             in.ShiftTime,
				OperationalShiftTimes: in.OperationalShiftTimes,
				Latitude:              in.Latitude,
				Longitude:             in.Longitude,
			},
		}
	}
	return BaseResource{
		Type: in.ResourceType,
		ID:   in.ID,
		Attributes: BaseAttributes{
			BaseName:                           row.BaseName,
			BaseAbbreviation:                   row.BaseAbbreviation,
			BaseCategory:                       in.BaseCategory,
			Address:                            row.Address,
			Latitude:                           row.Latitude,
			Longitude:                          row.Longitude,
			LandlineNumber:                     row.LandlineNumber,
			MobileNumber:                       row.MobileNumber,
			Email:                              row.Email,
			SortKey:                            cloneIntPtr(row.SortKey),
			SMSAlert:                           row.SMSAlert,
			Checklist:                          row.Checklist,
			LegTime:                            row.LegTime,
			DefaultStartTimeType:               normalizeShiftTimeType(row.DefaultStartTimeType),
			DefaultEndTimeType:                 normalizeShiftTimeType(row.DefaultEndTimeType),
			ShiftTime:                          in.ShiftTime,
			UTC:                                row.UTC,
			Foto:                               in.Foto,
			Dry:                                row.Dry,
			ControlCenter:                      row.ControlCenter,
			DUL:                                row.DUL,
			Notes:                              row.Notes,
			HEMSEDCContactIDs: in.HEMSEDCContactIDs,
			HEMSEDCs:          in.HEMSEDCs,
			MedPaxContactIDs:  in.MedPaxContactIDs,
			MedPax:            in.MedPax,
			ResponsiblePilotContactIDs:         in.ResponsiblePilotContactIDs,
			ResponsiblePilots:                  in.ResponsiblePilots,
			OperationalShiftTimes:              in.OperationalShiftTimes,
			IsActive:                           row.IsActive,
			CreatedBy:                          bytesToUUID(row.CreatedBy),
			UpdatedBy:                          bytesToUUID(row.UpdatedBy),
			CreatedAt:                          row.CreatedAt.UTC().Format(time.RFC3339),
			UpdatedAt:                          row.UpdatedAt.UTC().Format(time.RFC3339),
			DeletedAt:                          timePtrToString(row.DeletedAt),
			DeletedBy:                          bytesToUUID(row.DeletedBy),
		},
	}
}

func stringOrDefault(v *string, d string) string {
	if v == nil {
		return d
	}
	return *v
}

func boolOrDefault(v *bool, d bool) bool {
	if v == nil {
		return d
	}
	return *v
}

func normalizeUTC(v *string) string {
	s := strings.TrimSpace(stringOrDefault(v, "0"))
	if s == "" {
		return "0"
	}
	return s
}

func floatOrDefault(v *float64, d float64) float64 {
	if v == nil {
		return d
	}
	return *v
}

func cloneIntPtr(v *int) *int {
	if v == nil {
		return nil
	}
	x := *v
	return &x
}

func bytesToUUID(v []byte) string {
	if len(v) == 0 {
		return ""
	}
	if name := userctx.GetDisplayName(context.TODO(), v); name != "" {
		return name
	}
	s, err := uuidv7.BytesToString(v)
	if err != nil {
		return ""
	}
	return s
}

func timePtrToString(v *time.Time) string {
	if v == nil {
		return ""
	}
	return v.UTC().Format(time.RFC3339)
}
