package calendar

import (
	"net/mail"
	"strconv"
	"strings"
	"time"

	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
	"github.com/ultisuite/ulti-backend/internal/nextcloud"
)

const maxRequestBody = 256 << 10

type freeBusyRequest struct {
	Start     string   `json:"start"`
	End       string   `json:"end"`
	Attendees []string `json:"attendees"`
}

type respondInvitationRequest struct {
	Email    string `json:"email"`
	Response string `json:"response"`
	IfMatch  string `json:"if_match,omitempty"`
}

type normalizedInvitationResponse struct {
	Email    string
	Response string
	IfMatch  string
}

type createMeetLinkRequest struct {
	IfMatch string `json:"if_match,omitempty"`
}

func validateFreeBusy(req *freeBusyRequest) (*nextcloud.FreeBusyRequest, *apivalidate.ValidationError) {
	var details []apivalidate.FieldDetail

	start, startDetail := parseRFC3339Field("start", req.Start)
	if startDetail != nil {
		details = append(details, *startDetail)
	}
	end, endDetail := parseRFC3339Field("end", req.End)
	if endDetail != nil {
		details = append(details, *endDetail)
	}
	if startDetail == nil && endDetail == nil && !start.Before(end) {
		details = append(details, apivalidate.FieldDetail{Field: "end", Message: "must be after start"})
	}

	if len(req.Attendees) == 0 {
		details = append(details, apivalidate.FieldDetail{Field: "attendees", Message: "required"})
	} else {
		for i, addr := range req.Attendees {
			if detail := validateAttendeeEmail(addr); detail != nil {
				detail.Field = "attendees[" + strconv.Itoa(i) + "]"
				details = append(details, *detail)
			}
		}
	}

	if len(details) > 0 {
		return nil, apivalidate.NewValidationError(details...)
	}
	return &nextcloud.FreeBusyRequest{
		Start:     start,
		End:       end,
		Attendees: req.Attendees,
	}, nil
}

func parseRFC3339Field(field, raw string) (time.Time, *apivalidate.FieldDetail) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return time.Time{}, &apivalidate.FieldDetail{Field: field, Message: "required"}
	}
	t, err := time.Parse(time.RFC3339, raw)
	if err != nil {
		return time.Time{}, &apivalidate.FieldDetail{Field: field, Message: "invalid RFC3339 timestamp"}
	}
	return t.UTC(), nil
}

func validateAttendeeEmail(addr string) *apivalidate.FieldDetail {
	addr = strings.TrimSpace(addr)
	if addr == "" {
		return &apivalidate.FieldDetail{Field: "email", Message: "required"}
	}
	if len(addr) > 320 || strings.ContainsAny(addr, "\r\n") {
		return &apivalidate.FieldDetail{Field: "email", Message: "invalid"}
	}
	parsed, err := mail.ParseAddress(addr)
	if err != nil || parsed.Address == "" {
		return &apivalidate.FieldDetail{Field: "email", Message: "invalid"}
	}
	return nil
}

func validateRespondInvitation(req *respondInvitationRequest) (*normalizedInvitationResponse, *apivalidate.ValidationError) {
	var details []apivalidate.FieldDetail
	email := strings.TrimSpace(req.Email)
	if detail := validateAttendeeEmail(email); detail != nil {
		detail.Field = "email"
		details = append(details, *detail)
	}
	resp := strings.ToLower(strings.TrimSpace(req.Response))
	switch resp {
	case "accepted", "declined", "tentative":
	default:
		details = append(details, apivalidate.FieldDetail{Field: "response", Message: "must be one of accepted|declined|tentative"})
	}
	if strings.ContainsAny(req.IfMatch, "\r\n") {
		details = append(details, apivalidate.FieldDetail{Field: "if_match", Message: "invalid"})
	}
	if len(details) > 0 {
		return nil, apivalidate.NewValidationError(details...)
	}
	return &normalizedInvitationResponse{
		Email:    email,
		Response: strings.ToUpper(resp),
		IfMatch:  strings.TrimSpace(req.IfMatch),
	}, nil
}

func validateCreateMeetLink(req *createMeetLinkRequest) *apivalidate.ValidationError {
	if strings.ContainsAny(req.IfMatch, "\r\n") {
		return apivalidate.NewValidationError(apivalidate.FieldDetail{Field: "if_match", Message: "invalid"})
	}
	return nil
}

func validateCreateEvent(event *nextcloud.Event) *apivalidate.ValidationError {
	var details []apivalidate.FieldDetail
	if strings.TrimSpace(event.Summary) == "" {
		details = append(details, apivalidate.FieldDetail{Field: "summary", Message: "required"})
	}
	if strings.TrimSpace(event.Start) == "" {
		details = append(details, apivalidate.FieldDetail{Field: "start", Message: "required"})
	}
	if strings.TrimSpace(event.End) == "" {
		details = append(details, apivalidate.FieldDetail{Field: "end", Message: "required"})
	}
	for i, attendee := range event.Attendees {
		if detail := validateAttendeeEmail(attendee.Email); detail != nil {
			detail.Field = "attendees[" + strconv.Itoa(i) + "].email"
			details = append(details, *detail)
		}
	}
	if len(details) == 0 {
		return nil
	}
	return apivalidate.NewValidationError(details...)
}

func validateDeletePath(path string) *apivalidate.ValidationError {
	if strings.TrimSpace(path) == "" {
		return apivalidate.NewValidationError(apivalidate.FieldDetail{
			Field: "path", Message: "required",
		})
	}
	return nil
}
