package ai

import (
	"context"
	"log"
	"sync"
	"time"

	"golang.org/x/time/rate"

	"github.com/your-username/todo/src/config"
	"github.com/your-username/todo/src/database"
)

type BackgroundAnalyzer struct {
	db          database.Database
	aiService   *AIService
	config      *config.Config
	rateLimiter *rate.Limiter
	stats       *analyzerStats
}

type analyzerStats struct {
	mu              sync.Mutex
	totalProcessed  int64
	totalSuccesses  int64
	totalFailures   int64
	lastProcessed   time.Time
	lastError       error
	consecutiveErrs int
	backoffUntil    time.Time
}

func NewBackgroundAnalyzer(db database.Database, aiService *AIService, cfg *config.Config) *BackgroundAnalyzer {
	// Process one task per minute by default
	rps := rate.Every(time.Minute)
	if cfg.AI.RequestsPerMinute > 0 {
		rps = rate.Every(time.Minute / time.Duration(cfg.AI.RequestsPerMinute))
	}

	return &BackgroundAnalyzer{
		db:          db,
		aiService:   aiService,
		config:      cfg,
		rateLimiter: rate.NewLimiter(rps, 1), // Burst of 1 for background tasks
		stats:       &analyzerStats{},
	}
}

func (b *BackgroundAnalyzer) Start(ctx context.Context) {
	log.Printf("Starting background analyzer. Idle hours: %d:00-%d:00",
		b.config.UI.IdleStartHour,
		b.config.UI.IdleEndHour)

	ticker := time.NewTicker(time.Duration(b.config.UI.RefreshInterval) * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ctx.Done():
			log.Println("Background analyzer shutting down...")
			return
		case <-ticker.C:
			if !b.isIdleTime() {
				continue
			}

			if time.Now().Before(b.stats.backoffUntil) {
				continue
			}

			if b.stats.consecutiveErrs >= 5 {
				backoff := 5 * time.Minute * time.Duration(1<<uint(b.stats.consecutiveErrs-5))
				if backoff > 1*time.Hour {
					backoff = 1 * time.Hour
				}
				log.Printf("Too many consecutive errors (%d), backing off for %v",
					b.stats.consecutiveErrs, backoff)
				b.stats.backoffUntil = time.Now().Add(backoff)
				continue
			}

			b.processPendingTasks(ctx)
		}
	}
}

func (b *BackgroundAnalyzer) isIdleTime() bool {
	hour := time.Now().Hour()
	start := b.config.UI.IdleStartHour
	end := b.config.UI.IdleEndHour

	if start < end {
		return hour >= start && hour < end
	}
	// Handle overnight period (e.g., 22:00 - 06:00)
	return hour >= start || hour < end
}

func (b *BackgroundAnalyzer) processPendingTasks(ctx context.Context) {
	// Get tasks without analysis
	tasks, err := b.db.GetTasks(ctx, false)
	if err != nil {
		log.Printf("Error getting tasks: %v", err)
		b.recordError(err)
		return
	}

	for _, task := range tasks {
		// Check rate limit
		if err := b.rateLimiter.Wait(ctx); err != nil {
			log.Printf("Rate limit wait failed: %v", err)
			return
		}

		select {
		case <-ctx.Done():
			return
		default:
			// Check if task already has analysis
			analysis, err := b.db.GetTaskAnalysis(ctx, int(task.ID))
			if err != nil {
				log.Printf("Error checking task analysis for task %d: %v", task.ID, err)
				b.recordError(err)
				continue
			}
			if analysis != nil {
				continue
			}

			taskCtx, cancel := context.WithTimeout(ctx, 30*time.Second)

			// Analyze task
			result, err := b.aiService.AnalyzeTask(taskCtx, task.Title)
			cancel()

			if err != nil {
				log.Printf("Error analyzing task %d: %v", task.ID, err)
				b.recordError(err)
				continue
			}

			// Store analysis result
			jsonResult, err := result.ToJSON()
			if err != nil {
				log.Printf("Error serializing analysis for task %d: %v", task.ID, err)
				b.recordError(err)
				continue
			}

			err = b.db.StoreAnalysis(ctx, &database.Analysis{
				TaskID:     task.ID,
				Result:     jsonResult,
				AnalyzedAt: time.Now(),
			})
			if err != nil {
				log.Printf("Error storing analysis for task %d: %v", task.ID, err)
				b.recordError(err)
				continue
			}

			b.recordSuccess()
			log.Printf("Successfully analyzed task %d: %s", task.ID, result.Summary)
		}
	}
}

func (b *BackgroundAnalyzer) recordError(err error) {
	b.stats.mu.Lock()
	defer b.stats.mu.Unlock()

	b.stats.totalFailures++
	b.stats.consecutiveErrs++
	b.stats.lastError = err
	b.stats.lastProcessed = time.Now()
}

func (b *BackgroundAnalyzer) recordSuccess() {
	b.stats.mu.Lock()
	defer b.stats.mu.Unlock()

	b.stats.totalSuccesses++
	b.stats.totalProcessed++
	b.stats.consecutiveErrs = 0
	b.stats.lastProcessed = time.Now()
}

// GetStats returns current analyzer statistics
func (b *BackgroundAnalyzer) GetStats() *analyzerStats {
	b.stats.mu.Lock()
	defer b.stats.mu.Unlock()

	// Return a copy to prevent concurrent access issues
	return &analyzerStats{
		totalProcessed:  b.stats.totalProcessed,
		totalSuccesses:  b.stats.totalSuccesses,
		totalFailures:   b.stats.totalFailures,
		lastProcessed:   b.stats.lastProcessed,
		lastError:       b.stats.lastError,
		consecutiveErrs: b.stats.consecutiveErrs,
		backoffUntil:    b.stats.backoffUntil,
	}
}
