package mail

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

	"github.com/ultisuite/ulti-backend/internal/api/apiresponse"
	"github.com/ultisuite/ulti-backend/internal/api/apivalidate"
	"github.com/ultisuite/ulti-backend/internal/api/middleware"
	"github.com/ultisuite/ulti-backend/internal/api/query"
)

func (h *Handler) SearchMessages(w http.ResponseWriter, r *http.Request) {
	claims := middleware.ClaimsFromContext(r.Context())
	params, err := query.ParseList(stripNonDateListRangeKeys(r.URL.Query()))
	if err != nil {
		apivalidate.WriteQueryError(w, r, err)
		return
	}

	filter, verr := parseMessageSearchFilter(r)
	if verr != nil {
		apivalidate.WriteValidationError(w, r, verr)
		return
	}
	h.applyMailSearchScope(&filter, r)

	result, err := h.svc.SearchMessages(r.Context(), claims.Sub, filter, params)
	if err != nil {
		h.logger.Error("search messages", "error", err)
		apivalidate.WriteInternal(w, r)
		return
	}
	apiresponse.WriteJSON(w, http.StatusOK, result)
}

func parseMessageSearchFilter(r *http.Request) (MessageSearchFilter, *apivalidate.ValidationError) {
	q := r.URL.Query()
	filter := MessageSearchFilter{
		Query:     q.Get("q"),
		Sender:    parseSearchSender(q),
		Label:     q.Get("label"),
		AccountID: q.Get("account_id"),
	}

	if raw := q.Get("date_from"); raw != "" {
		t, err := time.Parse(time.RFC3339, raw)
		if err != nil {
			return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
				Field: "date_from", Message: "invalid RFC3339 datetime",
			})
		}
		filter.DateFrom = &t
	}
	if raw := q.Get("date_to"); raw != "" {
		t, err := time.Parse(time.RFC3339, raw)
		if err != nil {
			return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
				Field: "date_to", Message: "invalid RFC3339 datetime",
			})
		}
		filter.DateTo = &t
	}
	if raw := q.Get("has_attachment"); raw != "" {
		switch raw {
		case "true", "1":
			v := true
			filter.HasAttachments = &v
		case "false", "0":
			v := false
			filter.HasAttachments = &v
		default:
			return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
				Field: "has_attachment", Message: "must be true or false",
			})
		}
	}

	if filter.Query == "" && filter.Sender == "" && filter.Label == "" &&
		filter.AccountID == "" && filter.DateFrom == nil && filter.DateTo == nil &&
		filter.HasAttachments == nil {
		return filter, apivalidate.NewValidationError(apivalidate.FieldDetail{
			Field: "q", Message: "at least one search filter required",
		})
	}
	return filter, nil
}

// stripNonDateListRangeKeys removes from/to when they are sender/recipient filters,
// not YYYY-MM-DD list date bounds (shared param names on /mail/search).
func stripNonDateListRangeKeys(values url.Values) url.Values {
	out := values
	clone := make(url.Values, len(values))
	for k, vv := range values {
		clone[k] = append([]string(nil), vv...)
	}
	out = clone
	for _, key := range []string{"from", "to"} {
		raw := strings.TrimSpace(out.Get(key))
		if raw == "" {
			continue
		}
		if _, err := query.ParseDate(raw); err != nil {
			out.Del(key)
		}
	}
	return out
}

func parseSearchSender(q url.Values) string {
	if s := strings.TrimSpace(q.Get("sender")); s != "" {
		return s
	}
	from := strings.TrimSpace(q.Get("from"))
	if from == "" {
		return ""
	}
	if _, err := query.ParseDate(from); err != nil {
		return from
	}
	return ""
}
