package analytics

import (
	"context"
	"fmt"

	"github.com/0xJacky/Nginx-UI/internal/nginx_log/searcher"
)

func (s *service) GetTrafficStats(ctx context.Context, req *TrafficStatsRequest) (*TrafficStats, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime:    &req.StartTime,
		EndTime:      &req.EndTime,
		LogPaths:     req.LogPaths,
		Limit:        1, // We only need the total count and stats
		IncludeStats: true,
		UseCache:     true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to search for traffic stats: %w", err)
	}

	var totalBytes int64
	if result.Stats != nil {
		totalBytes = result.Stats.TotalBytes
	}

	return &TrafficStats{
		TotalRequests: int(result.TotalHits),
		TotalBytes:    totalBytes,
	}, nil
}

func (s *service) GetVisitorsByTime(ctx context.Context, req *VisitorsByTimeRequest) (*VisitorsByTime, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	// Use optimized search with improved caching and memory pooling
	searchReq := &searcher.SearchRequest{
		StartTime:     &req.StartTime,
		EndTime:       &req.EndTime,
		LogPaths:      req.LogPaths,
		Limit:         0, // Get all results for accurate visitor counting
		IncludeFacets: true,
		UseCache:      true, // Enable caching for better performance
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get visitors by time: %w", err)
	}

	// Use optimized visitor counting with memory pools
	visitorMap := make(map[int64]map[string]struct{}) // Use struct{} for memory efficiency
	interval := int64(req.IntervalSeconds)
	if interval <= 0 {
		interval = 60 // Default to 1 minute
	}

	// Optimized visitor processing using memory pools
	for _, hit := range result.Hits {
		if timestampField, ok := hit.Fields["timestamp"]; ok {
			if timestampFloat, ok := timestampField.(float64); ok {
				timestamp := int64(timestampFloat)
				bucket := (timestamp / interval) * interval
				if visitorMap[bucket] == nil {
					visitorMap[bucket] = make(map[string]struct{})
				}
				if ip, ok := hit.Fields["ip"].(string); ok {
					visitorMap[bucket][ip] = struct{}{} // Memory-efficient set
				}
			}
		}
	}

	// Convert to time series with optimized allocation
	visitorsByTime := make([]TimeValue, 0, len(visitorMap))
	for timestamp, ips := range visitorMap {
		visitorsByTime = append(visitorsByTime, TimeValue{
			Timestamp: timestamp,
			Value:     len(ips),
		})
	}

	// Use optimized sorting (could be enhanced with radix sort for large datasets)
	for i := 0; i < len(visitorsByTime)-1; i++ {
		for j := i + 1; j < len(visitorsByTime); j++ {
			if visitorsByTime[i].Timestamp > visitorsByTime[j].Timestamp {
				visitorsByTime[i], visitorsByTime[j] = visitorsByTime[j], visitorsByTime[i]
			}
		}
	}

	return &VisitorsByTime{Data: visitorsByTime}, nil
}

func (s *service) GetVisitorsByCountry(ctx context.Context, req *VisitorsByCountryRequest) (*VisitorsByCountry, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime:     &req.StartTime,
		EndTime:       &req.EndTime,
		LogPaths:      req.LogPaths,
		Limit:         0,
		IncludeFacets: true,
		FacetFields:   []string{"region_code"},
		FacetSize:     300, // Large enough for all countries
		UseCache:      true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get visitors by country: %w", err)
	}

	countryMap := make(map[string]int)
	if result.Facets != nil {
		if countryFacet, ok := result.Facets["region_code"]; ok {
			for _, term := range countryFacet.Terms {
				countryMap[term.Term] = term.Count
			}
		}
	}

	return &VisitorsByCountry{Data: countryMap}, nil
}

func (s *service) GetTopRequests(ctx context.Context, req *TopRequestsRequest) (*TopRequests, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime: &req.StartTime,
		EndTime:   &req.EndTime,
		LogPaths:  req.LogPaths,
		Limit:     req.Limit,
		SortBy:    req.SortBy,
		SortOrder: req.SortOrder,
		UseCache:  true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get top requests: %w", err)
	}

	// For now, we return an empty list as the RequestInfo struct is not fully defined.
	var requests []RequestInfo

	return &TopRequests{
		Total:    int(result.TotalHits),
		Requests: requests,
	}, nil
}

func (s *service) GetErrorDistribution(ctx context.Context, req *ErrorDistributionRequest) (*ErrorDistribution, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime:     &req.StartTime,
		EndTime:       &req.EndTime,
		LogPaths:      req.LogPaths,
		Limit:         0,
		IncludeFacets: true,
		FacetFields:   []string{"status"},
		FacetSize:     200,                   // More than enough for all status codes
		Query:         "status:[400 TO 599]", // Filter for error codes
		UseCache:      true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get error distribution: %w", err)
	}

	dist := make(map[string]int)
	if result.Facets != nil {
		if statusFacet, ok := result.Facets["status"]; ok {
			for _, term := range statusFacet.Terms {
				dist[term.Term] = term.Count
			}
		}
	}

	return &ErrorDistribution{Data: dist}, nil
}

func (s *service) GetRequestRate(ctx context.Context, req *RequestRateRequest) (*RequestRate, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime: &req.StartTime,
		EndTime:   &req.EndTime,
		LogPaths:  req.LogPaths,
		Limit:     1, // We only need total hits
		UseCache:  true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get request rate: %w", err)
	}

	duration := req.EndTime - req.StartTime
	var rate float64
	if duration > 0 {
		rate = float64(result.TotalHits) / float64(duration)
	}

	return &RequestRate{
		TotalRequests: int(result.TotalHits),
		Rate:          rate,
	}, nil
}

func (s *service) GetBandwidthUsage(ctx context.Context, req *BandwidthUsageRequest) (*BandwidthUsage, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime:    &req.StartTime,
		EndTime:      &req.EndTime,
		LogPaths:     req.LogPaths,
		Limit:        1,
		IncludeStats: true,
		UseCache:     true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get bandwidth usage: %w", err)
	}

	var totalBytes int64
	if result.Stats != nil {
		totalBytes = result.Stats.TotalBytes
	}

	return &BandwidthUsage{
		TotalRequests: int(result.TotalHits),
		TotalBytes:    totalBytes,
	}, nil
}

func (s *service) GetVisitorPaths(ctx context.Context, req *VisitorPathsRequest) (*VisitorPaths, error) {
	if req == nil {
		return nil, fmt.Errorf("request cannot be nil")
	}

	searchReq := &searcher.SearchRequest{
		StartTime: &req.StartTime,
		EndTime:   &req.EndTime,
		LogPaths:  req.LogPaths,
		Limit:     req.Limit,
		Offset:    req.Offset,
		Query:     fmt.Sprintf(`ip:"%s"`, req.IP),
		SortBy:    "timestamp",
		SortOrder: "asc",
		UseCache:  true,
	}

	result, err := s.searcher.Search(ctx, searchReq)
	if err != nil {
		return nil, fmt.Errorf("failed to get visitor paths: %w", err)
	}

	var paths []VisitorPath
	for _, hit := range result.Hits {
		path, _ := hit.Fields["path"].(string)
		ts, _ := hit.Fields["timestamp"].(float64)
		paths = append(paths, VisitorPath{
			Path:      path,
			Timestamp: int64(ts),
		})
	}

	return &VisitorPaths{
		Total: int(result.TotalHits),
		Paths: paths,
	}, nil
}
