package app

import (
	"context"
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"hyapp/pkg/appcode"
	mysqlstorage "hyapp/services/statistics-service/internal/storage/mysql"
)

type queryHTTPServer struct {
	repo     *mysqlstorage.Repository
	server   *http.Server
	listener net.Listener
}

func newQueryHTTPServer(addr string, repo *mysqlstorage.Repository) (*queryHTTPServer, error) {
	addr = strings.TrimSpace(addr)
	if addr == "" {
		return nil, nil
	}
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return nil, err
	}
	s := &queryHTTPServer{repo: repo, listener: listener}
	mux := http.NewServeMux()
	mux.HandleFunc("/internal/v1/statistics/overview", s.overview)
	mux.HandleFunc("/internal/v1/statistics/platform-grants/users", s.platformGrantUsers)
	mux.HandleFunc("/internal/v1/statistics/platform-grants/records", s.platformGrantRecords)
	mux.HandleFunc("/internal/v1/statistics/self-game/events", s.selfGameEvents)
	mux.HandleFunc("/internal/v1/statistics/self-games/overview", s.selfGameOverview)
	s.server = &http.Server{Handler: mux, ReadHeaderTimeout: 2 * time.Second}
	return s, nil
}

func (s *queryHTTPServer) Run() error {
	if s == nil {
		return nil
	}
	err := s.server.Serve(s.listener)
	if errors.Is(err, http.ErrServerClosed) {
		return nil
	}
	return err
}

func (s *queryHTTPServer) Close(ctx context.Context) error {
	if s == nil {
		return nil
	}
	return s.server.Shutdown(ctx)
}

func (s *queryHTTPServer) overview(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	startMS := parseInt64(query.Get("start_ms"))
	endMS := parseInt64(query.Get("end_ms"))
	if startMS <= 0 {
		now := time.Now().UTC()
		startMS = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).UnixMilli()
	}
	if endMS <= startMS {
		endMS = startMS + 24*time.Hour.Milliseconds()
	}
	overview, err := s.repo.QueryOverview(r.Context(), mysqlstorage.OverviewQuery{
		AppCode:       query.Get("app_code"),
		StatTZ:        query.Get("stat_tz"),
		StartMS:       startMS,
		EndMS:         endMS,
		SeriesStartMS: parseInt64(query.Get("series_start_ms")),
		SeriesEndMS:   parseInt64(query.Get("series_end_ms")),
		CountryID:     parseInt64(query.Get("country_id")),
		RegionID:      parseInt64(query.Get("region_id")),
	})
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, overview)
}

func (s *queryHTTPServer) platformGrantUsers(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	pageSize := parsePositiveInt(query.Get("page_size"))
	if pageSize == 0 {
		pageSize = parsePositiveInt(query.Get("pageSize"))
	}
	page, err := s.repo.QueryPlatformGrantCoinUsers(r.Context(), mysqlstorage.PlatformGrantCoinQuery{
		AppCode:   query.Get("app_code"),
		StatTZ:    query.Get("stat_tz"),
		StartMS:   parseInt64(query.Get("start_ms")),
		EndMS:     parseInt64(query.Get("end_ms")),
		StatDay:   query.Get("stat_day"),
		CountryID: parseInt64(query.Get("country_id")),
		RegionID:  parseInt64(query.Get("region_id")),
		EventType: firstNonEmpty(query.Get("source"), query.Get("event_type")),
		Page:      parsePositiveInt(query.Get("page")),
		PageSize:  pageSize,
	})
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, page)
}

func (s *queryHTTPServer) platformGrantRecords(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	userID := parseInt64(query.Get("user_id"))
	if userID <= 0 {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": "user_id is required"})
		return
	}
	pageSize := parsePositiveInt(query.Get("page_size"))
	if pageSize == 0 {
		pageSize = parsePositiveInt(query.Get("pageSize"))
	}
	page, err := s.repo.QueryPlatformGrantCoinRecords(r.Context(), mysqlstorage.PlatformGrantCoinQuery{
		AppCode:   query.Get("app_code"),
		StatTZ:    query.Get("stat_tz"),
		StartMS:   parseInt64(query.Get("start_ms")),
		EndMS:     parseInt64(query.Get("end_ms")),
		StatDay:   query.Get("stat_day"),
		CountryID: parseInt64(query.Get("country_id")),
		RegionID:  parseInt64(query.Get("region_id")),
		UserID:    userID,
		EventType: firstNonEmpty(query.Get("source"), query.Get("event_type")),
		Page:      parsePositiveInt(query.Get("page")),
		PageSize:  pageSize,
	})
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, page)
}

type selfGameH5EventRequest struct {
	AppCode       string `json:"app_code"`
	EventID       string `json:"event_id"`
	EventName     string `json:"event_name"`
	GameID        string `json:"game_id"`
	Page          string `json:"page"`
	SessionID     string `json:"session_id"`
	MatchID       string `json:"match_id"`
	UserID        int64  `json:"user_id"`
	CountryID     int64  `json:"country_id"`
	RegionID      int64  `json:"region_id"`
	Language      string `json:"language"`
	ClientVersion string `json:"client_version"`
	H5Version     string `json:"h5_version"`
	EntrySource   string `json:"entry_source"`
	DurationMS    int64  `json:"duration_ms"`
	Success       bool   `json:"success"`
	ErrorCode     string `json:"error_code"`
	OccurredAtMS  int64  `json:"occurred_at_ms"`
}

func (s *queryHTTPServer) selfGameEvents(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		writeJSON(w, http.StatusMethodNotAllowed, map[string]any{"error": "method not allowed"})
		return
	}
	defer r.Body.Close()
	var body selfGameH5EventRequest
	if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid json"})
		return
	}
	body.EventID = strings.TrimSpace(body.EventID)
	body.EventName = strings.TrimSpace(body.EventName)
	if body.EventID == "" || body.EventName == "" {
		writeJSON(w, http.StatusBadRequest, map[string]any{"error": "event_id and event_name are required"})
		return
	}
	if body.OccurredAtMS <= 0 {
		body.OccurredAtMS = time.Now().UTC().UnixMilli()
	}
	app := appcode.Normalize(body.AppCode)
	if app == "" {
		app = "lalu"
	}
	// 内部 ingest 只承接 gateway 已解析过的用户、App 和维度字段；statistics-service 只做幂等聚合，不反查业务库。
	err := s.repo.ConsumeSelfGameH5Event(appcode.WithContext(r.Context(), app), mysqlstorage.SelfGameH5Event{
		AppCode:       app,
		EventID:       body.EventID,
		EventName:     body.EventName,
		GameID:        body.GameID,
		Page:          body.Page,
		SessionID:     body.SessionID,
		MatchID:       body.MatchID,
		UserID:        body.UserID,
		CountryID:     body.CountryID,
		RegionID:      body.RegionID,
		Language:      body.Language,
		ClientVersion: body.ClientVersion,
		H5Version:     body.H5Version,
		EntrySource:   body.EntrySource,
		DurationMS:    body.DurationMS,
		Success:       body.Success,
		ErrorCode:     body.ErrorCode,
		OccurredAtMS:  body.OccurredAtMS,
	})
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, map[string]any{"ok": true})
}

func (s *queryHTTPServer) selfGameOverview(w http.ResponseWriter, r *http.Request) {
	query := r.URL.Query()
	startMS := parseInt64(query.Get("start_ms"))
	endMS := parseInt64(query.Get("end_ms"))
	if startMS <= 0 {
		now := time.Now().UTC()
		startMS = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC).UnixMilli()
	}
	if endMS <= startMS {
		endMS = startMS + 24*time.Hour.Milliseconds()
	}
	overview, err := s.repo.QuerySelfGameOverview(r.Context(), mysqlstorage.SelfGameOverviewQuery{
		AppCode:   query.Get("app_code"),
		StatTZ:    query.Get("stat_tz"),
		StartMS:   startMS,
		EndMS:     endMS,
		GameID:    query.Get("game_id"),
		CountryID: parseInt64(query.Get("country_id")),
		RegionID:  parseInt64(query.Get("region_id")),
	})
	if err != nil {
		writeJSON(w, http.StatusInternalServerError, map[string]any{"error": err.Error()})
		return
	}
	writeJSON(w, http.StatusOK, overview)
}

func parseInt64(value string) int64 {
	parsed, _ := strconv.ParseInt(strings.TrimSpace(value), 10, 64)
	return parsed
}

func parsePositiveInt(value string) int {
	parsed, _ := strconv.Atoi(strings.TrimSpace(value))
	if parsed < 0 {
		return 0
	}
	return parsed
}

func writeJSON(w http.ResponseWriter, status int, payload any) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(payload)
}
