package service

import (
	"context"
	"net/http"
	"strings"
	"time"

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

type BaseWriteError struct {
	Status  int
	Title   string
	Detail  string
	Pointer string
}

func (e *BaseWriteError) Error() string {
	if e == nil {
		return ""
	}
	return e.Detail
}

type BaseWriteResult struct {
	Row                      *base.Base
	CategoryType             string
	PreviousFotoAttachmentID []byte
}

type BaseOperationalShiftTimeInput struct {
	DateStart     time.Time
	DateEnd       time.Time
	StartTimeType string
	EndTimeType   string
	ShiftStart    *time.Time
	ShiftEnd      *time.Time
}

type BaseCreateInput struct {
	ID                                 []byte
	CategoryType                       string
	BaseName                           string
	BaseAbbreviation                   string
	Address                            string
	Latitude                           float64
	Longitude                          float64
	LandlineNumber                     string
	MobileNumber                       string
	Email                              string
	SortKey                            *int
	SMSAlert                           bool
	Checklist                          bool
	LegTime                            string
	DefaultStartTimeType               string
	DefaultEndTimeType                 string
	DefaultShiftStart                  *time.Time
	DefaultShiftEnd                    *time.Time
	UTC                                string
	Dry                                bool
	ControlCenter                      bool
	DUL                                bool
	Notes                              string
	HEMSEDCContactIDs          [][]byte
	MedPaxContactIDs           [][]byte
	ResponsiblePilotContactIDs [][]byte
	OperationalShiftTimes              []BaseOperationalShiftTimeInput
	IsActive                           bool
	CreatedBy                          []byte
	UpdatedBy                          []byte
	FotoAttachmentID                   []byte
}

type BaseUpdateInput struct {
	ID                                    []byte
	CategoryType                          string
	BaseName                              *string
	BaseAbbreviation                      *string
	Address                               *string
	Latitude                              *float64
	Longitude                             *float64
	LandlineNumber                        *string
	MobileNumber                          *string
	Email                                 *string
	SortKeySet                            bool
	SortKeyValid                          bool
	SortKeyValue                          *int
	SMSAlert                              *bool
	Checklist                             *bool
	LegTime                               *string
	DefaultStartTimeType                  *string
	DefaultEndTimeType                    *string
	DefaultShiftStart                     *time.Time
	DefaultShiftEnd                       *time.Time
	UTC                                   *string
	Dry                                   *bool
	ControlCenter                         *bool
	DUL                                   *bool
	Notes                                 *string
	HEMSEDCContactIDs      [][]byte
	HEMSEDCContactIDsSet   bool
	MedPaxContactIDs       [][]byte
	MedPaxContactIDsSet    bool
	ResponsiblePilotContactIDs            [][]byte
	ResponsiblePilotContactIDsSet         bool
	OperationalShiftTimes                 []BaseOperationalShiftTimeInput
	OperationalShiftTimesSet              bool
	IsActive                              *bool
	CreatedBy                             []byte
	CreatedBySet                          bool
	UpdatedBy                             []byte
	UpdatedBySet                          bool
	FotoAttachmentID                      []byte
	FotoAttachmentSet                     bool
}

func (s *BaseService) CreateDetailed(ctx context.Context, in BaseCreateInput) (*BaseWriteResult, error) {
	if s == nil || s.repo == nil {
		return nil, &BaseWriteError{Status: http.StatusInternalServerError, Title: "Create failed", Detail: "base service is not configured"}
	}
	category, ok := base.NormalizeCategoryType(in.CategoryType)
	if !ok {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "invalid base category type", Pointer: "/data/attributes/base_category"}
	}
	baseName := strings.TrimSpace(in.BaseName)
	if baseName == "" {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "base is required", Pointer: "/data/attributes/base"}
	}
	if in.Latitude < -90 || in.Latitude > 90 {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "latitude must be between -90 and 90", Pointer: "/data/attributes/latitude"}
	}
	if in.Longitude < -180 || in.Longitude > 180 {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "longitude must be between -180 and 180", Pointer: "/data/attributes/longitude"}
	}
	defaultStartTimeType := normalizeShiftTimeType(in.DefaultStartTimeType)
	defaultEndTimeType := normalizeShiftTimeType(in.DefaultEndTimeType)
	if defaultStartTimeType == base.ShiftTimeTypeFixed && in.DefaultShiftStart == nil {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "default_shift_start is required", Pointer: "/data/attributes/default_shift_start"}
	}
	if defaultEndTimeType == base.ShiftTimeTypeFixed && in.DefaultShiftEnd == nil {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "default_shift_end is required", Pointer: "/data/attributes/default_shift_end"}
	}

	row := &base.Base{
		ID:                                 append([]byte(nil), in.ID...),
		BaseName:                           baseName,
		BaseAbbreviation:                   strings.TrimSpace(in.BaseAbbreviation),
		Address:                            strings.TrimSpace(in.Address),
		Latitude:                           in.Latitude,
		Longitude:                          in.Longitude,
		LandlineNumber:                     strings.TrimSpace(in.LandlineNumber),
		MobileNumber:                       strings.TrimSpace(in.MobileNumber),
		Email:                              strings.TrimSpace(in.Email),
		SortKey:                            cloneIntPtr(in.SortKey),
		SMSAlert:                           in.SMSAlert,
		Checklist:                          in.Checklist,
		LegTime:                            strings.TrimSpace(in.LegTime),
		DefaultStartTimeType:               defaultStartTimeType,
		DefaultEndTimeType:                 defaultEndTimeType,
		DefaultShiftStart:                  timePtrToClockString(in.DefaultShiftStart),
		DefaultShiftEnd:                    timePtrToClockString(in.DefaultShiftEnd),
		UTC:                                normalizeUTCString(in.UTC),
		Dry:                                in.Dry,
		ControlCenter:                      in.ControlCenter,
		DUL:                                in.DUL,
		Notes:                              strings.TrimSpace(in.Notes),
		IsActive:                           in.IsActive,
		HEMSEDCContactIDs: appendUniqueIDs(in.HEMSEDCContactIDs),
		MedPaxContactIDs:  appendUniqueIDs(in.MedPaxContactIDs),
		ResponsiblePilotContactIDs:         appendUniqueIDs(in.ResponsiblePilotContactIDs),
		OperationalShiftTimes:              cloneOperationalShiftTimeInputs(in.OperationalShiftTimes),
		CreatedBy:                          append([]byte(nil), in.CreatedBy...),
		UpdatedBy:                          append([]byte(nil), in.UpdatedBy...),
		FotoAttachmentID:                   append([]byte(nil), in.FotoAttachmentID...),
	}
	if len(row.ID) == 0 {
		row.ID = uuidv7.MustBytes()
	}
	if err := s.CreateBase(ctx, row, category); err != nil {
		return nil, &BaseWriteError{Status: http.StatusBadRequest, Title: "Create failed", Detail: err.Error()}
	}
	created, err := s.GetBaseByID(ctx, row.ID, category)
	if err != nil {
		return nil, &BaseWriteError{Status: http.StatusBadRequest, Title: "Create failed", Detail: err.Error()}
	}
	if created == nil {
		created = row
	}
	return &BaseWriteResult{Row: created, CategoryType: category}, nil
}

func (s *BaseService) UpdateDetailed(ctx context.Context, in BaseUpdateInput) (*BaseWriteResult, error) {
	if s == nil || s.repo == nil {
		return nil, &BaseWriteError{Status: http.StatusInternalServerError, Title: "Update failed", Detail: "base service is not configured"}
	}
	category, ok := base.NormalizeCategoryType(in.CategoryType)
	if !ok {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "invalid base category type", Pointer: "/data/attributes/base_category"}
	}
	if len(in.ID) != 16 {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "uuid is invalid UUID", Pointer: "/path/uuid"}
	}

	existing, err := s.GetBaseByID(ctx, in.ID, category)
	if err != nil {
		return nil, &BaseWriteError{Status: http.StatusNotFound, Title: "Not found", Detail: categoryNotFoundDetail(category)}
	}
	if existing == nil {
		return nil, &BaseWriteError{Status: http.StatusNotFound, Title: "Not found", Detail: categoryNotFoundDetail(category)}
	}
	prevFotoAttachmentID := append([]byte(nil), existing.FotoAttachmentID...)
	if in.Latitude != nil && (*in.Latitude < -90 || *in.Latitude > 90) {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "latitude must be between -90 and 90", Pointer: "/data/attributes/latitude"}
	}
	if in.Longitude != nil && (*in.Longitude < -180 || *in.Longitude > 180) {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "longitude must be between -180 and 180", Pointer: "/data/attributes/longitude"}
	}

	if in.BaseName != nil {
		v := strings.TrimSpace(*in.BaseName)
		if v == "" {
			return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "base cannot be empty", Pointer: "/data/attributes/base"}
		}
		existing.BaseName = v
	}
	if in.BaseAbbreviation != nil {
		existing.BaseAbbreviation = strings.TrimSpace(*in.BaseAbbreviation)
	}
	if in.Address != nil {
		existing.Address = strings.TrimSpace(*in.Address)
	}
	if in.Latitude != nil {
		existing.Latitude = *in.Latitude
	}
	if in.Longitude != nil {
		existing.Longitude = *in.Longitude
	}
	if in.LandlineNumber != nil {
		existing.LandlineNumber = strings.TrimSpace(*in.LandlineNumber)
	}
	if in.MobileNumber != nil {
		existing.MobileNumber = strings.TrimSpace(*in.MobileNumber)
	}
	if in.Email != nil {
		existing.Email = strings.TrimSpace(*in.Email)
	}
	if in.SortKeySet {
		if in.SortKeyValid {
			existing.SortKey = cloneIntPtr(in.SortKeyValue)
		} else {
			existing.SortKey = nil
		}
	}
	if in.SMSAlert != nil {
		existing.SMSAlert = *in.SMSAlert
	}
	if in.Checklist != nil {
		existing.Checklist = *in.Checklist
	}
	if in.LegTime != nil {
		existing.LegTime = strings.TrimSpace(*in.LegTime)
	}
	if in.DefaultStartTimeType != nil {
		existing.DefaultStartTimeType = normalizeShiftTimeType(*in.DefaultStartTimeType)
	}
	if in.DefaultEndTimeType != nil {
		existing.DefaultEndTimeType = normalizeShiftTimeType(*in.DefaultEndTimeType)
	}
	if in.DefaultShiftStart != nil {
		existing.DefaultShiftStart = timePtrToClockString(in.DefaultShiftStart)
	}
	if in.DefaultShiftEnd != nil {
		existing.DefaultShiftEnd = timePtrToClockString(in.DefaultShiftEnd)
	}
	if normalizeShiftTimeType(existing.DefaultStartTimeType) != base.ShiftTimeTypeFixed {
		existing.DefaultShiftStart = ""
	}
	if normalizeShiftTimeType(existing.DefaultEndTimeType) != base.ShiftTimeTypeFixed {
		existing.DefaultShiftEnd = ""
	}
	if normalizeShiftTimeType(existing.DefaultStartTimeType) == base.ShiftTimeTypeFixed && strings.TrimSpace(existing.DefaultShiftStart) == "" {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "default_shift_start is required", Pointer: "/data/attributes/default_shift_start"}
	}
	if normalizeShiftTimeType(existing.DefaultEndTimeType) == base.ShiftTimeTypeFixed && strings.TrimSpace(existing.DefaultShiftEnd) == "" {
		return nil, &BaseWriteError{Status: http.StatusUnprocessableEntity, Title: "Validation error", Detail: "default_shift_end is required", Pointer: "/data/attributes/default_shift_end"}
	}
	if in.UTC != nil {
		existing.UTC = normalizeUTCString(*in.UTC)
	}
	if in.Dry != nil {
		existing.Dry = *in.Dry
	}
	if in.ControlCenter != nil {
		existing.ControlCenter = *in.ControlCenter
	}
	if in.DUL != nil {
		existing.DUL = *in.DUL
	}
	if in.Notes != nil {
		existing.Notes = strings.TrimSpace(*in.Notes)
	}
	if in.HEMSEDCContactIDsSet {
		existing.HEMSEDCContactIDs = appendUniqueIDs(in.HEMSEDCContactIDs)
	}
	if in.MedPaxContactIDsSet {
		existing.MedPaxContactIDs = appendUniqueIDs(in.MedPaxContactIDs)
	}
	if in.ResponsiblePilotContactIDsSet {
		existing.ResponsiblePilotContactIDs = appendUniqueIDs(in.ResponsiblePilotContactIDs)
	}
	if in.OperationalShiftTimesSet {
		existing.OperationalShiftTimes = cloneOperationalShiftTimeInputs(in.OperationalShiftTimes)
		if existing.OperationalShiftTimes == nil {
			existing.OperationalShiftTimes = []base.BaseOperationalShiftTime{}
		}
	} else {
		existing.OperationalShiftTimes = nil
	}
	if in.CreatedBySet {
		existing.CreatedBy = append([]byte(nil), in.CreatedBy...)
	}
	if in.UpdatedBySet {
		existing.UpdatedBy = append([]byte(nil), in.UpdatedBy...)
	}
	if in.FotoAttachmentSet {
		existing.FotoAttachmentID = append([]byte(nil), in.FotoAttachmentID...)
	}
	if in.IsActive != nil {
		existing.IsActive = *in.IsActive
	}

	if err := s.UpdateBase(ctx, existing, category); err != nil {
		return nil, &BaseWriteError{Status: http.StatusBadRequest, Title: "Update failed", Detail: err.Error()}
	}
	updated, err := s.GetBaseByID(ctx, existing.ID, category)
	if err != nil {
		return nil, &BaseWriteError{Status: http.StatusBadRequest, Title: "Update failed", Detail: err.Error()}
	}
	if updated == nil {
		updated = existing
	}
	return &BaseWriteResult{Row: updated, CategoryType: category, PreviousFotoAttachmentID: prevFotoAttachmentID}, nil
}

func cloneOperationalShiftTimeInputs(items []BaseOperationalShiftTimeInput) []base.BaseOperationalShiftTime {
	if len(items) == 0 {
		return nil
	}
	out := make([]base.BaseOperationalShiftTime, 0, len(items))
	for i := range items {
		dateStart := items[i].DateStart.UTC()
		dateEnd := items[i].DateEnd.UTC()
		out = append(out, base.BaseOperationalShiftTime{
			DateStart:     &dateStart,
			DateEnd:       &dateEnd,
			StartTimeType: normalizeShiftTimeType(items[i].StartTimeType),
			EndTimeType:   normalizeShiftTimeType(items[i].EndTimeType),
			ShiftStart:    timePtrToClockString(items[i].ShiftStart),
			ShiftEnd:      timePtrToClockString(items[i].ShiftEnd),
		})
	}
	return out
}

func timePtrToClockString(v *time.Time) string {
	if v == nil {
		return ""
	}
	return v.UTC().Format("15:04:05")
}

func normalizeUTCString(raw string) string {
	return strings.TrimSpace(raw)
}

func normalizeShiftTimeType(raw string) string {
	switch strings.ToUpper(strings.TrimSpace(raw)) {
	case "", base.ShiftTimeTypeFixed:
		return base.ShiftTimeTypeFixed
	case base.ShiftTimeTypeBMCT:
		return base.ShiftTimeTypeBMCT
	case base.ShiftTimeTypeECET:
		return base.ShiftTimeTypeECET
	default:
		return strings.ToUpper(strings.TrimSpace(raw))
	}
}

func appendUniqueIDs(values [][]byte) [][]byte {
	if len(values) == 0 {
		return nil
	}
	out := make([][]byte, 0, len(values))
	seen := map[string]struct{}{}
	for i := range values {
		if len(values[i]) != 16 {
			continue
		}
		key := string(values[i])
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}
		out = append(out, append([]byte(nil), values[i]...))
	}
	return out
}

func categoryNotFoundDetail(category string) string {
	if category == base.CategoryKeyHEMS {
		return "hems base not found"
	}
	return "base not found"
}
