package dashboard

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"hyapp-admin-server/internal/config"
	"hyapp-admin-server/internal/integration/userclient"
	"hyapp-admin-server/internal/repository"
)

type DashboardService struct {
	store      *repository.Store
	cfg        config.Config
	httpClient *http.Client
	user       userclient.Client
}

type StatisticsQuery struct {
	AppCode       string
	StartMS       int64
	EndMS         int64
	SeriesStartMS int64
	SeriesEndMS   int64
	RegionID      int64
	CountryID     int64
}

type SelfGameStatisticsQuery struct {
	AppCode   string
	RequestID string
	StartMS   int64
	EndMS     int64
	RegionID  int64
	CountryID int64
	GameID    string
}

func NewService(store *repository.Store, cfg config.Config, user userclient.Client) *DashboardService {
	return &DashboardService{
		store:      store,
		cfg:        cfg,
		httpClient: &http.Client{Timeout: cfg.StatisticsService.RequestTimeout},
		user:       user,
	}
}

func (s *DashboardService) Overview() (*repository.DashboardOverview, error) {
	return s.store.DashboardOverview()
}

func (s *DashboardService) StatisticsOverview(ctx context.Context, query StatisticsQuery) (map[string]any, error) {
	values := url.Values{}
	values.Set("app_code", query.AppCode)
	if query.StartMS > 0 {
		values.Set("start_ms", strconv.FormatInt(query.StartMS, 10))
	}
	if query.EndMS > 0 {
		values.Set("end_ms", strconv.FormatInt(query.EndMS, 10))
	}
	if query.SeriesStartMS > 0 {
		values.Set("series_start_ms", strconv.FormatInt(query.SeriesStartMS, 10))
	}
	if query.SeriesEndMS > 0 {
		values.Set("series_end_ms", strconv.FormatInt(query.SeriesEndMS, 10))
	}
	if query.RegionID > 0 {
		values.Set("region_id", strconv.FormatInt(query.RegionID, 10))
	}
	if query.CountryID > 0 {
		values.Set("country_id", strconv.FormatInt(query.CountryID, 10))
	}
	return s.statisticsGET(ctx, "/internal/v1/statistics/overview", values)
}

func (s *DashboardService) SelfGameStatisticsOverview(ctx context.Context, query SelfGameStatisticsQuery) (map[string]any, error) {
	values := url.Values{}
	values.Set("app_code", query.AppCode)
	if query.StartMS > 0 {
		values.Set("start_ms", strconv.FormatInt(query.StartMS, 10))
	}
	if query.EndMS > 0 {
		values.Set("end_ms", strconv.FormatInt(query.EndMS, 10))
	}
	if query.RegionID > 0 {
		values.Set("region_id", strconv.FormatInt(query.RegionID, 10))
	}
	if query.CountryID > 0 {
		values.Set("country_id", strconv.FormatInt(query.CountryID, 10))
	}
	if gameID := strings.TrimSpace(query.GameID); gameID != "" && gameID != "all" {
		values.Set("game_id", gameID)
	}
	// 数据大屏只通过 statistics-service 读取自研游戏聚合指标，避免 admin 服务绕过聚合层去扫业务明细表。
	overview, err := s.statisticsGET(ctx, "/internal/v1/statistics/self-games/overview", values)
	if err != nil {
		return nil, err
	}
	// 高风险用户只对 statistics-service 已返回的 topN user_id 做批量资料补全；头像、昵称、短 ID 不进入统计库，避免聚合链路反查用户表。
	_ = s.enrichSelfGameRiskUsers(ctx, query, overview)
	return overview, nil
}

func (s *DashboardService) statisticsGET(ctx context.Context, path string, values url.Values) (map[string]any, error) {
	baseURL := s.cfg.StatisticsService.BaseURL + path
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, baseURL+"?"+values.Encode(), nil)
	if err != nil {
		return nil, err
	}
	resp, err := s.httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return nil, fmt.Errorf("statistics service returned status %d", resp.StatusCode)
	}
	var out map[string]any
	decoder := json.NewDecoder(resp.Body)
	decoder.UseNumber()
	if err := decoder.Decode(&out); err != nil {
		return nil, err
	}
	return out, nil
}

func (s *DashboardService) enrichSelfGameRiskUsers(ctx context.Context, query SelfGameStatisticsQuery, overview map[string]any) error {
	if s.user == nil || overview == nil {
		return nil
	}
	rows, ok := overview["user_risk"].([]any)
	if !ok || len(rows) == 0 {
		return nil
	}
	userIDs := make([]int64, 0, len(rows))
	seen := map[int64]struct{}{}
	for _, raw := range rows {
		row, ok := raw.(map[string]any)
		if !ok {
			continue
		}
		userID := anyInt64(row["user_id"])
		if userID <= 0 {
			continue
		}
		// user_id 是 16 位以上业务 ID，admin 层统一改成字符串透给 H5，避免浏览器 JSON number 精度丢失。
		row["user_id"] = strconv.FormatInt(userID, 10)
		if _, exists := seen[userID]; exists {
			continue
		}
		seen[userID] = struct{}{}
		userIDs = append(userIDs, userID)
	}
	if len(userIDs) == 0 {
		return nil
	}
	users, err := s.user.BatchGetUsers(ctx, userclient.BatchGetUsersRequest{
		RequestID: query.RequestID,
		Caller:    "admin-server",
		UserIDs:   userIDs,
	})
	if err != nil {
		return err
	}
	for _, raw := range rows {
		row, ok := raw.(map[string]any)
		if !ok {
			continue
		}
		user := users[anyInt64(row["user_id"])]
		if user == nil {
			continue
		}
		row["avatar"] = user.Avatar
		row["nickname"] = user.Username
		row["username"] = user.Username
		row["display_user_id"] = user.DisplayUserID
		row["short_id"] = user.DisplayUserID
	}
	return nil
}

func anyInt64(value any) int64 {
	switch typed := value.(type) {
	case int64:
		return typed
	case int:
		return int64(typed)
	case float64:
		return int64(typed)
	case json.Number:
		parsed, _ := typed.Int64()
		return parsed
	case string:
		parsed, _ := strconv.ParseInt(strings.TrimSpace(typed), 10, 64)
		return parsed
	default:
		return 0
	}
}
