package agencyopening

import (
	"strconv"
	"strings"
	"time"

	"hyapp-admin-server/internal/appctx"
	"hyapp-admin-server/internal/integration/activityclient"
	"hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/modules/shared"
	"hyapp-admin-server/internal/response"
	activityv1 "hyapp.local/api/proto/activity/v1"

	"github.com/gin-gonic/gin"
)

type Handler struct {
	activity activityclient.Client
	audit    shared.OperationLogger
}

func New(activity activityclient.Client, audit shared.OperationLogger) *Handler {
	return &Handler{activity: activity, audit: audit}
}

type cycleRequest struct {
	CycleID          string      `json:"cycle_id"`
	RegionID         int64       `json:"region_id"`
	Title            string      `json:"title"`
	Status           string      `json:"status"`
	StartMS          int64       `json:"start_ms"`
	EndMS            int64       `json:"end_ms"`
	MinHostCount     int32       `json:"min_host_count"`
	MaxAgencyAgeDays int32       `json:"max_agency_age_days"`
	Rewards          []rewardDTO `json:"rewards"`
}

type statusRequest struct {
	Status string `json:"status"`
}

type cycleDTO struct {
	AppCode          string      `json:"app_code,omitempty"`
	CycleID          string      `json:"cycle_id"`
	RegionID         int64       `json:"region_id"`
	ActivityCode     string      `json:"activity_code"`
	Title            string      `json:"title"`
	Status           string      `json:"status"`
	StartMS          int64       `json:"start_ms"`
	EndMS            int64       `json:"end_ms"`
	MinHostCount     int32       `json:"min_host_count"`
	MaxAgencyAgeDays int32       `json:"max_agency_age_days"`
	Rewards          []rewardDTO `json:"rewards"`
	CreatedByAdminID int64       `json:"created_by_admin_id"`
	UpdatedByAdminID int64       `json:"updated_by_admin_id"`
	SettledAtMS      int64       `json:"settled_at_ms"`
	CreatedAtMS      int64       `json:"created_at_ms"`
	UpdatedAtMS      int64       `json:"updated_at_ms"`
}

type rewardDTO struct {
	RankNo             int32 `json:"rank_no"`
	TierNo             int32 `json:"tier_no,omitempty"`
	ThresholdCoinSpent int64 `json:"threshold_coin_spent"`
	RewardCoinAmount   int64 `json:"reward_coin_amount"`
}

type applicationDTO struct {
	ApplicationID       string `json:"application_id"`
	CycleID             string `json:"cycle_id"`
	RegionID            int64  `json:"region_id"`
	AgencyID            int64  `json:"agency_id,string"`
	AgencyOwnerUserID   int64  `json:"agency_owner_user_id,string"`
	AgencyName          string `json:"agency_name"`
	HostCount           int32  `json:"host_count"`
	AgencyCreatedAtMS   int64  `json:"agency_created_at_ms"`
	Status              string `json:"status"`
	ScoreCoinAmount     int64  `json:"score_coin_amount"`
	RewardRankNo        int32  `json:"reward_rank_no"`
	RewardThresholdCoin int64  `json:"reward_threshold_coin_spent"`
	RewardCoinAmount    int64  `json:"reward_coin_amount"`
	WalletCommandID     string `json:"wallet_command_id"`
	WalletTransactionID string `json:"wallet_transaction_id"`
	FailureReason       string `json:"failure_reason"`
	AppliedAtMS         int64  `json:"applied_at_ms"`
	ApprovedAtMS        int64  `json:"approved_at_ms"`
	ScoreStartMS        int64  `json:"score_start_ms"`
	ScoreEndMS          int64  `json:"score_end_ms"`
	ReviewedByAdminID   int64  `json:"reviewed_by_admin_id"`
	GrantedAtMS         int64  `json:"granted_at_ms"`
	UpdatedAtMS         int64  `json:"updated_at_ms"`
}

func (h *Handler) ListCycles(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.activity.ListAgencyOpeningCycles(c.Request.Context(), &activityv1.ListAgencyOpeningCyclesRequest{
		Meta:     h.meta(c),
		RegionId: parseInt64Query(c, "region_id"),
		Status:   strings.TrimSpace(options.Status),
		StartMs:  parseInt64Query(c, "start_ms"),
		EndMs:    parseInt64Query(c, "end_ms"),
		Page:     int32(options.Page),
		PageSize: int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取代理开业周期失败")
		return
	}
	items := make([]cycleDTO, 0, len(resp.GetCycles()))
	for _, cycle := range resp.GetCycles() {
		items = append(items, cycleFromProto(cycle))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) CreateCycle(c *gin.Context) {
	var req cycleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "代理开业周期参数不正确")
		return
	}
	if req.RegionID < 0 {
		response.BadRequest(c, "区域参数不正确")
		return
	}
	resp, err := h.activity.CreateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{
		Meta:            h.meta(c),
		Cycle:           req.toProto(),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "create-agency-opening-cycle", "agency_opening_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) GetCycle(c *gin.Context) {
	cycleID := strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.GetAgencyOpeningCycle(c.Request.Context(), &activityv1.GetAgencyOpeningCycleRequest{Meta: h.meta(c), CycleId: cycleID})
	if err != nil {
		response.ServerError(c, "获取代理开业周期详情失败")
		return
	}
	response.OK(c, cycleFromProto(resp.GetCycle()))
}

func (h *Handler) UpdateCycle(c *gin.Context) {
	var req cycleRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "代理开业周期参数不正确")
		return
	}
	if req.RegionID < 0 {
		response.BadRequest(c, "区域参数不正确")
		return
	}
	req.CycleID = strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.UpdateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{
		Meta:            h.meta(c),
		Cycle:           req.toProto(),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "update-agency-opening-cycle", "agency_opening_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) SetCycleStatus(c *gin.Context) {
	var req statusRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "代理开业周期状态参数不正确")
		return
	}
	cycleID := strings.TrimSpace(c.Param("cycle_id"))
	resp, err := h.activity.SetAgencyOpeningCycleStatus(c.Request.Context(), &activityv1.SetAgencyOpeningCycleStatusRequest{
		Meta:            h.meta(c),
		CycleId:         cycleID,
		Status:          strings.TrimSpace(req.Status),
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := cycleFromProto(resp.GetCycle())
	shared.OperationLogWithResourceID(c, h.audit, "set-agency-opening-cycle-status", "agency_opening_cycles", item.CycleID, "success", "")
	response.OK(c, item)
}

func (h *Handler) ListApplications(c *gin.Context) {
	options := shared.ListOptions(c)
	resp, err := h.activity.ListAgencyOpeningApplications(c.Request.Context(), &activityv1.ListAgencyOpeningApplicationsRequest{
		Meta:              h.meta(c),
		CycleId:           strings.TrimSpace(c.Query("cycle_id")),
		RegionId:          parseInt64Query(c, "region_id"),
		Status:            strings.TrimSpace(options.Status),
		AgencyId:          parseInt64Query(c, "agency_id"),
		AgencyOwnerUserId: parseInt64Query(c, "agency_owner_user_id"),
		Page:              int32(options.Page),
		PageSize:          int32(options.PageSize),
	})
	if err != nil {
		response.ServerError(c, "获取代理开业申请记录失败")
		return
	}
	items := make([]applicationDTO, 0, len(resp.GetApplications()))
	for _, application := range resp.GetApplications() {
		items = append(items, applicationFromProto(application))
	}
	response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()})
}

func (h *Handler) ApproveApplication(c *gin.Context) {
	applicationID := strings.TrimSpace(c.Param("application_id"))
	resp, err := h.activity.ReviewAgencyOpeningApplication(c.Request.Context(), &activityv1.ReviewAgencyOpeningApplicationRequest{
		Meta:            h.meta(c),
		ApplicationId:   applicationID,
		Action:          "approve",
		OperatorAdminId: int64(middleware.CurrentUserID(c)),
	})
	if err != nil {
		response.BadRequest(c, err.Error())
		return
	}
	item := applicationFromProto(resp.GetApplication())
	shared.OperationLogWithResourceID(c, h.audit, "approve-agency-opening-application", "agency_opening_applications", item.ApplicationID, "success", "")
	response.OK(c, item)
}

func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta {
	return &activityv1.RequestMeta{
		RequestId: middleware.CurrentRequestID(c),
		Caller:    "admin-server",
		AppCode:   appctx.FromContext(c.Request.Context()),
		SentAtMs:  time.Now().UnixMilli(),
	}
}

func (r cycleRequest) toProto() *activityv1.AgencyOpeningCycle {
	rewards := make([]*activityv1.AgencyOpeningReward, 0, len(r.Rewards))
	for _, reward := range r.Rewards {
		rankNo := reward.RankNo
		if rankNo <= 0 {
			rankNo = reward.TierNo
		}
		rewards = append(rewards, &activityv1.AgencyOpeningReward{
			RankNo:             rankNo,
			ThresholdCoinSpent: reward.ThresholdCoinSpent,
			RewardCoinAmount:   reward.RewardCoinAmount,
		})
	}
	return &activityv1.AgencyOpeningCycle{
		CycleId:          strings.TrimSpace(r.CycleID),
		RegionId:         r.RegionID,
		Title:            strings.TrimSpace(r.Title),
		Status:           strings.TrimSpace(r.Status),
		StartMs:          r.StartMS,
		EndMs:            r.EndMS,
		MinHostCount:     r.MinHostCount,
		MaxAgencyAgeDays: r.MaxAgencyAgeDays,
		Rewards:          rewards,
	}
}

func cycleFromProto(cycle *activityv1.AgencyOpeningCycle) cycleDTO {
	if cycle == nil {
		return cycleDTO{Rewards: []rewardDTO{}}
	}
	rewards := make([]rewardDTO, 0, len(cycle.GetRewards()))
	for _, reward := range cycle.GetRewards() {
		rewards = append(rewards, rewardDTO{
			RankNo:             reward.GetRankNo(),
			TierNo:             reward.GetRankNo(),
			ThresholdCoinSpent: reward.GetThresholdCoinSpent(),
			RewardCoinAmount:   reward.GetRewardCoinAmount(),
		})
	}
	return cycleDTO{
		AppCode:          cycle.GetAppCode(),
		CycleID:          cycle.GetCycleId(),
		RegionID:         cycle.GetRegionId(),
		ActivityCode:     cycle.GetActivityCode(),
		Title:            cycle.GetTitle(),
		Status:           cycle.GetStatus(),
		StartMS:          cycle.GetStartMs(),
		EndMS:            cycle.GetEndMs(),
		MinHostCount:     cycle.GetMinHostCount(),
		MaxAgencyAgeDays: cycle.GetMaxAgencyAgeDays(),
		Rewards:          rewards,
		CreatedByAdminID: cycle.GetCreatedByAdminId(),
		UpdatedByAdminID: cycle.GetUpdatedByAdminId(),
		SettledAtMS:      cycle.GetSettledAtMs(),
		CreatedAtMS:      cycle.GetCreatedAtMs(),
		UpdatedAtMS:      cycle.GetUpdatedAtMs(),
	}
}

func applicationFromProto(item *activityv1.AgencyOpeningApplication) applicationDTO {
	if item == nil {
		return applicationDTO{}
	}
	return applicationDTO{
		ApplicationID:       item.GetApplicationId(),
		CycleID:             item.GetCycleId(),
		RegionID:            item.GetRegionId(),
		AgencyID:            item.GetAgencyId(),
		AgencyOwnerUserID:   item.GetAgencyOwnerUserId(),
		AgencyName:          item.GetAgencyName(),
		HostCount:           item.GetHostCount(),
		AgencyCreatedAtMS:   item.GetAgencyCreatedAtMs(),
		Status:              item.GetStatus(),
		ScoreCoinAmount:     item.GetScoreCoinAmount(),
		RewardRankNo:        item.GetRewardRankNo(),
		RewardThresholdCoin: item.GetRewardThresholdCoinSpent(),
		RewardCoinAmount:    item.GetRewardCoinAmount(),
		WalletCommandID:     item.GetWalletCommandId(),
		WalletTransactionID: item.GetWalletTransactionId(),
		FailureReason:       item.GetFailureReason(),
		AppliedAtMS:         item.GetAppliedAtMs(),
		ApprovedAtMS:        item.GetApprovedAtMs(),
		ScoreStartMS:        item.GetScoreStartMs(),
		ScoreEndMS:          item.GetScoreEndMs(),
		ReviewedByAdminID:   item.GetReviewedByAdminId(),
		GrantedAtMS:         item.GetGrantedAtMs(),
		UpdatedAtMS:         item.GetUpdatedAtMs(),
	}
}

func parseInt64Query(c *gin.Context, key string) int64 {
	value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64)
	return value
}
