package track3

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

	"github.com/ethereum/go-ethereum/common"
	"github.com/explorer/backend/analytics"
	"github.com/jackc/pgx/v5/pgxpool"
)

// Server handles Track 3 endpoints
type Server struct {
	db              *pgxpool.Pool
	flowTracker     *analytics.FlowTracker
	bridgeAnalytics *analytics.BridgeAnalytics
	tokenDist       *analytics.TokenDistribution
	riskAnalyzer    *analytics.AddressRiskAnalyzer
	chainID         int
}

// NewServer creates a new Track 3 server
func NewServer(db *pgxpool.Pool, chainID int) *Server {
	return &Server{
		db:              db,
		flowTracker:     analytics.NewFlowTracker(db, chainID),
		bridgeAnalytics: analytics.NewBridgeAnalytics(db),
		tokenDist:       analytics.NewTokenDistribution(db, chainID),
		riskAnalyzer:    analytics.NewAddressRiskAnalyzer(db, chainID),
		chainID:         chainID,
	}
}

// HandleFlows handles GET /api/v1/track3/analytics/flows
func (s *Server) HandleFlows(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	from, err := normalizeTrack3OptionalAddress(r.URL.Query().Get("from"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}
	to, err := normalizeTrack3OptionalAddress(r.URL.Query().Get("to"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}
	token, err := normalizeTrack3OptionalAddress(r.URL.Query().Get("token"))
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}
	limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
	if limit < 1 || limit > 200 {
		limit = 50
	}

	var startDate, endDate *time.Time
	if startStr := r.URL.Query().Get("start_date"); startStr != "" {
		t, err := time.Parse(time.RFC3339, startStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid start_date")
			return
		}
		startDate = &t
	}
	if endStr := r.URL.Query().Get("end_date"); endStr != "" {
		t, err := time.Parse(time.RFC3339, endStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid end_date")
			return
		}
		endDate = &t
	}

	flows, err := s.flowTracker.GetFlows(r.Context(), from, to, token, startDate, endDate, limit)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "database_error", err.Error())
		return
	}

	response := map[string]interface{}{
		"data": map[string]interface{}{
			"flows": flows,
		},
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// HandleBridge handles GET /api/v1/track3/analytics/bridge
func (s *Server) HandleBridge(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	var chainFrom, chainTo *int
	if cf := r.URL.Query().Get("chain_from"); cf != "" {
		c, err := strconv.Atoi(cf)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid chain_from")
			return
		}
		chainFrom = &c
	}
	if ct := r.URL.Query().Get("chain_to"); ct != "" {
		c, err := strconv.Atoi(ct)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid chain_to")
			return
		}
		chainTo = &c
	}

	var startDate, endDate *time.Time
	if startStr := r.URL.Query().Get("start_date"); startStr != "" {
		t, err := time.Parse(time.RFC3339, startStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid start_date")
			return
		}
		startDate = &t
	}
	if endStr := r.URL.Query().Get("end_date"); endStr != "" {
		t, err := time.Parse(time.RFC3339, endStr)
		if err != nil {
			writeError(w, http.StatusBadRequest, "bad_request", "invalid end_date")
			return
		}
		endDate = &t
	}

	stats, err := s.bridgeAnalytics.GetBridgeStats(r.Context(), chainFrom, chainTo, startDate, endDate)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "database_error", err.Error())
		return
	}

	response := map[string]interface{}{
		"data": stats,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// HandleTokenDistribution handles GET /api/v1/track3/analytics/token-distribution
func (s *Server) HandleTokenDistribution(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/track3/analytics/token-distribution/")
	contract, err := normalizeTrack3RequiredAddress(path)
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}

	topN, _ := strconv.Atoi(r.URL.Query().Get("top_n"))
	if topN < 1 || topN > 1000 {
		topN = 100
	}

	stats, err := s.tokenDist.GetTokenDistribution(r.Context(), contract, topN)
	if err != nil {
		writeError(w, http.StatusNotFound, "not_found", err.Error())
		return
	}

	response := map[string]interface{}{
		"data": stats,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

// HandleAddressRisk handles GET /api/v1/track3/analytics/address-risk/:addr
func (s *Server) HandleAddressRisk(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeError(w, http.StatusMethodNotAllowed, "method_not_allowed", "Method not allowed")
		return
	}
	if !s.requireDB(w) {
		return
	}

	path := strings.TrimPrefix(r.URL.Path, "/api/v1/track3/analytics/address-risk/")
	address, err := normalizeTrack3RequiredAddress(path)
	if err != nil {
		writeError(w, http.StatusBadRequest, "bad_request", err.Error())
		return
	}

	analysis, err := s.riskAnalyzer.AnalyzeAddress(r.Context(), address)
	if err != nil {
		writeError(w, http.StatusInternalServerError, "database_error", err.Error())
		return
	}

	response := map[string]interface{}{
		"data": analysis,
	}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(response)
}

func writeError(w http.ResponseWriter, statusCode int, code, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(statusCode)
	json.NewEncoder(w).Encode(map[string]interface{}{
		"error": map[string]interface{}{
			"code":    code,
			"message": message,
		},
	})
}

func (s *Server) requireDB(w http.ResponseWriter) bool {
	if s.db == nil {
		writeError(w, http.StatusServiceUnavailable, "service_unavailable", "database not configured")
		return false
	}
	return true
}

func normalizeTrack3OptionalAddress(value string) (string, error) {
	trimmed := strings.TrimSpace(value)
	if trimmed == "" {
		return "", nil
	}
	if !common.IsHexAddress(trimmed) {
		return "", fmt.Errorf("invalid address format")
	}
	return strings.ToLower(common.HexToAddress(trimmed).Hex()), nil
}

func normalizeTrack3RequiredAddress(value string) (string, error) {
	normalized, err := normalizeTrack3OptionalAddress(value)
	if err != nil {
		return "", err
	}
	if normalized == "" {
		return "", fmt.Errorf("address required")
	}
	return normalized, nil
}
