package userapi

import (
	"encoding/json"
	"net/http"
	"strconv"
	"strings"

	userv1 "hyapp.local/api/proto/user/v1"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

type hostAgencyData struct {
	AgencyID        string `json:"agency_id"`
	OwnerUserID     string `json:"owner_user_id"`
	RegionID        int64  `json:"region_id"`
	ParentBDUserID  string `json:"parent_bd_user_id,omitempty"`
	Name            string `json:"name"`
	Avatar          string `json:"avatar,omitempty"`
	HostCount       int32  `json:"host_count"`
	Status          string `json:"status"`
	JoinEnabled     bool   `json:"join_enabled"`
	CreatedByUserID string `json:"created_by_user_id,omitempty"`
	CreatedAtMS     int64  `json:"created_at_ms"`
	UpdatedAtMS     int64  `json:"updated_at_ms"`
}

type hostAgencyApplicationData struct {
	ApplicationID    string `json:"application_id"`
	CommandID        string `json:"command_id"`
	ApplicantUserID  string `json:"applicant_user_id"`
	AgencyID         string `json:"agency_id"`
	RegionID         int64  `json:"region_id"`
	Status           string `json:"status"`
	ReviewedByUserID string `json:"reviewed_by_user_id,omitempty"`
	ReviewReason     string `json:"review_reason,omitempty"`
	ReviewedAtMS     int64  `json:"reviewed_at_ms,omitempty"`
	CreatedAtMS      int64  `json:"created_at_ms"`
	UpdatedAtMS      int64  `json:"updated_at_ms"`
}

func (h *Handler) searchHostAgencies(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	keyword := strings.TrimSpace(request.URL.Query().Get("short_id"))
	if keyword == "" {
		keyword = strings.TrimSpace(request.URL.Query().Get("keyword"))
	}
	pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 100)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	if pageSize > 100 {
		pageSize = 100
	}
	resp, err := h.userHostClient.SearchAgencies(request.Context(), &userv1.SearchAgenciesRequest{
		Meta:     httpkit.UserMeta(request, ""),
		UserId:   auth.UserIDFromContext(request.Context()),
		Keyword:  keyword,
		PageSize: pageSize,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	items := make([]hostAgencyData, 0, len(resp.GetAgencies()))
	for _, agency := range resp.GetAgencies() {
		items = append(items, hostAgencyFromProto(agency))
	}
	httpkit.WriteOK(writer, request, map[string]any{"items": items, "total": len(items), "page_size": pageSize})
}

func (h *Handler) applyToHostAgency(writer http.ResponseWriter, request *http.Request) {
	if h.userHostClient == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	var body struct {
		CommandID    string          `json:"command_id"`
		CommandIDAlt string          `json:"commandId"`
		AgencyID     json.RawMessage `json:"agency_id"`
		AgencyIDAlt  json.RawMessage `json:"agencyId"`
	}
	if !httpkit.Decode(writer, request, &body) {
		return
	}
	commandID := strings.TrimSpace(body.CommandID)
	if commandID == "" {
		commandID = strings.TrimSpace(body.CommandIDAlt)
	}
	agencyID, ok := firstRawInt64(body.AgencyID, body.AgencyIDAlt)
	if commandID == "" || !ok || agencyID <= 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	resp, err := h.userHostClient.ApplyToAgency(request.Context(), &userv1.ApplyToAgencyRequest{
		Meta:      httpkit.UserMeta(request, ""),
		CommandId: commandID,
		UserId:    auth.UserIDFromContext(request.Context()),
		AgencyId:  agencyID,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	httpkit.WriteOK(writer, request, map[string]any{"application": hostAgencyApplicationFromProto(resp.GetApplication())})
}

func hostAgencyFromProto(agency *userv1.Agency) hostAgencyData {
	if agency == nil {
		return hostAgencyData{}
	}
	return hostAgencyData{
		AgencyID:        int64String(agency.GetAgencyId()),
		OwnerUserID:     int64String(agency.GetOwnerUserId()),
		RegionID:        agency.GetRegionId(),
		ParentBDUserID:  int64String(agency.GetParentBdUserId()),
		Name:            agency.GetName(),
		Avatar:          agency.GetAvatar(),
		HostCount:       agency.GetHostCount(),
		Status:          agency.GetStatus(),
		JoinEnabled:     agency.GetJoinEnabled(),
		CreatedByUserID: int64String(agency.GetCreatedByUserId()),
		CreatedAtMS:     agency.GetCreatedAtMs(),
		UpdatedAtMS:     agency.GetUpdatedAtMs(),
	}
}

func hostAgencyApplicationFromProto(application *userv1.AgencyApplication) hostAgencyApplicationData {
	if application == nil {
		return hostAgencyApplicationData{}
	}
	return hostAgencyApplicationData{
		ApplicationID:    int64String(application.GetApplicationId()),
		CommandID:        application.GetCommandId(),
		ApplicantUserID:  int64String(application.GetApplicantUserId()),
		AgencyID:         int64String(application.GetAgencyId()),
		RegionID:         application.GetRegionId(),
		Status:           application.GetStatus(),
		ReviewedByUserID: int64String(application.GetReviewedByUserId()),
		ReviewReason:     application.GetReviewReason(),
		ReviewedAtMS:     application.GetReviewedAtMs(),
		CreatedAtMS:      application.GetCreatedAtMs(),
		UpdatedAtMS:      application.GetUpdatedAtMs(),
	}
}

func int64String(value int64) string {
	if value <= 0 {
		return ""
	}
	return strconv.FormatInt(value, 10)
}

func firstRawInt64(values ...json.RawMessage) (int64, bool) {
	for _, raw := range values {
		value, ok := parseRawInt64(raw)
		if ok {
			return value, true
		}
	}
	return 0, false
}

func parseRawInt64(raw json.RawMessage) (int64, bool) {
	text := strings.TrimSpace(string(raw))
	if text == "" || text == "null" {
		return 0, false
	}
	if strings.HasPrefix(text, `"`) {
		var value string
		if err := json.Unmarshal(raw, &value); err != nil {
			return 0, false
		}
		text = strings.TrimSpace(value)
	}
	value, err := strconv.ParseInt(text, 10, 64)
	return value, err == nil
}
