// Package engine provides the core bot engine that wires all components together.
package engine

import (
	"context"
	"fmt"
	"image"
	"log"
	"sync"
	"time"

	"git.cloonar.com/openclawd/iso-bot/pkg/engine/capture"
	"git.cloonar.com/openclawd/iso-bot/pkg/engine/resolution"
	"git.cloonar.com/openclawd/iso-bot/pkg/engine/state"
	"git.cloonar.com/openclawd/iso-bot/pkg/engine/loot"
	"git.cloonar.com/openclawd/iso-bot/pkg/plugin"
)

// Engine is the core bot engine that coordinates all subsystems.
type Engine struct {
	mu             sync.RWMutex
	running        bool
	paused         bool
	devMode        bool
	
	// Core components
	captureManager     *capture.Manager
	resolutionRegistry *resolution.Registry
	stateManager       *state.Manager
	lootEngine         *loot.RuleEngine
	
	// Plugin system
	gamePlugin plugin.Plugin
	services   *engineServices
	
	// Current state
	currentFrame image.Image
	gameState    plugin.GameState
	vitals       plugin.VitalStats
	
	// Statistics
	frameCount   uint64
	itemsFound   int
	runCount     int
	startTime    time.Time
	
	// Control channels
	stopChan   chan struct{}
	pauseChan  chan bool
}

// NewEngine creates a new bot engine.
func NewEngine(captureSource capture.Source, gamePlugin plugin.Plugin, devMode bool) (*Engine, error) {
	engine := &Engine{
		captureManager:     capture.NewManager(captureSource),
		resolutionRegistry: resolution.NewRegistry(),
		devMode:           devMode,
		gamePlugin:        gamePlugin,
		stopChan:          make(chan struct{}),
		pauseChan:         make(chan bool, 1),
		startTime:         time.Now(),
	}
	
	// Register game-specific resolution profiles with the engine's registry.
	// Plugins that support resolution profiles implement this optional interface.
	type profileRegistrar interface {
		RegisterResolutionProfiles(*resolution.Registry) error
	}
	if registrar, ok := gamePlugin.(profileRegistrar); ok {
		if err := registrar.RegisterResolutionProfiles(engine.resolutionRegistry); err != nil {
			return nil, fmt.Errorf("failed to register resolution profiles: %w", err)
		}
		log.Printf("Registered resolution profiles for %s", gamePlugin.Info().ID)
	}
	
	// Create engine services for the plugin
	engine.services = &engineServices{engine: engine}
	
	// Initialize the plugin
	if err := gamePlugin.Init(engine.services); err != nil {
		return nil, fmt.Errorf("failed to initialize game plugin: %w", err)
	}
	
	// Set up state manager
	engine.stateManager = state.NewManager(gamePlugin.Detector())
	
	// Set up default loot filter
	if lootFilter := gamePlugin.DefaultLootFilter(); lootFilter != nil {
		if ruleEngine, ok := lootFilter.(*loot.RuleEngine); ok {
			engine.lootEngine = ruleEngine
		}
	}
	
	return engine, nil
}

// Start begins the engine's main loop.
func (e *Engine) Start(ctx context.Context) error {
	e.mu.Lock()
	if e.running {
		e.mu.Unlock()
		return fmt.Errorf("engine is already running")
	}
	e.running = true
	e.mu.Unlock()
	
	log.Printf("Engine starting (dev mode: %v)", e.devMode)
	
	ticker := time.NewTicker(33 * time.Millisecond) // ~30 FPS
	defer ticker.Stop()
	
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-e.stopChan:
			return nil
		case paused := <-e.pauseChan:
			e.mu.Lock()
			e.paused = paused
			e.mu.Unlock()
			if paused {
				log.Println("Engine paused")
			} else {
				log.Println("Engine resumed")
			}
		case <-ticker.C:
			if e.isPaused() {
				continue
			}
			
			if err := e.processFrame(); err != nil {
				log.Printf("Frame processing error: %v", err)
				continue
			}
		}
	}
}

// Stop stops the engine.
func (e *Engine) Stop() {
	e.mu.Lock()
	defer e.mu.Unlock()
	
	if !e.running {
		return
	}
	
	e.running = false
	close(e.stopChan)
	log.Println("Engine stopped")
}

// Pause pauses or resumes the engine.
func (e *Engine) Pause(paused bool) {
	select {
	case e.pauseChan <- paused:
	default:
		// Channel is full, already have a pending pause state
	}
}

// Status returns the current engine status.
func (e *Engine) Status() EngineStatus {
	e.mu.RLock()
	defer e.mu.RUnlock()
	
	return EngineStatus{
		Running:     e.running,
		Paused:      e.paused,
		DevMode:     e.devMode,
		GameState:   e.gameState,
		Vitals:      e.vitals,
		FrameCount:  e.frameCount,
		ItemsFound:  e.itemsFound,
		RunCount:    e.runCount,
		Uptime:      time.Since(e.startTime),
		CaptureStats: e.captureManager.Stats(),
	}
}

// CurrentFrame returns the most recent captured frame.
func (e *Engine) CurrentFrame() image.Image {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.currentFrame
}

// GamePlugin returns the active game plugin.
func (e *Engine) GamePlugin() plugin.Plugin {
	return e.gamePlugin
}

// ResolutionRegistry returns the resolution registry.
func (e *Engine) ResolutionRegistry() *resolution.Registry {
	return e.resolutionRegistry
}

// LootEngine returns the loot filter engine.
func (e *Engine) LootEngine() *loot.RuleEngine {
	return e.lootEngine
}

// SetCaptureSource swaps the capture source (for development/testing).
func (e *Engine) SetCaptureSource(newSource capture.Source) error {
	e.mu.Lock()
	defer e.mu.Unlock()
	
	return e.captureManager.SetSource(newSource)
}

// processFrame captures and analyzes a single frame.
func (e *Engine) processFrame() error {
	// Capture frame
	frame, err := e.captureManager.Capture()
	if err != nil {
		return fmt.Errorf("capture failed: %w", err)
	}
	
	e.mu.Lock()
	e.currentFrame = frame
	e.frameCount++
	e.mu.Unlock()
	
	// Update game state
	gameState := e.stateManager.Update(frame)
	
	// Read vitals if in-game
	var vitals plugin.VitalStats
	if gameState == plugin.StateInGame {
		vitals = e.gamePlugin.Detector().ReadVitals(frame)
	}
	
	e.mu.Lock()
	e.gameState = gameState
	e.vitals = vitals
	e.mu.Unlock()
	
	// In dev mode, we don't perform any actions
	if e.devMode {
		return nil
	}
	
	// TODO: Implement bot actions based on state and detected items
	
	return nil
}

// isPaused returns true if the engine is paused.
func (e *Engine) isPaused() bool {
	e.mu.RLock()
	defer e.mu.RUnlock()
	return e.paused
}

// EngineStatus represents the current state of the engine.
type EngineStatus struct {
	Running      bool                `json:"running"`
	Paused       bool                `json:"paused"`
	DevMode      bool                `json:"devMode"`
	GameState    plugin.GameState    `json:"gameState"`
	Vitals       plugin.VitalStats   `json:"vitals"`
	FrameCount   uint64              `json:"frameCount"`
	ItemsFound   int                 `json:"itemsFound"`
	RunCount     int                 `json:"runCount"`
	Uptime       time.Duration       `json:"uptime"`
	CaptureStats capture.Stats       `json:"captureStats"`
}

// engineServices implements plugin.EngineServices.
type engineServices struct {
	engine *Engine
}

// Capture returns the current screen frame.
func (s *engineServices) Capture() image.Image {
	return s.engine.CurrentFrame()
}

// CaptureSource returns the active capture source.
func (s *engineServices) CaptureSource() capture.Source {
	return s.engine.captureManager.Source()
}

// Resolution returns the current capture resolution.
func (s *engineServices) Resolution() (width, height int) {
	return s.engine.captureManager.Size()
}

// Region returns a named screen region for the current game and resolution.
func (s *engineServices) Region(name string) image.Rectangle {
	width, height := s.Resolution()
	gameID := s.engine.gamePlugin.Info().ID
	
	region, err := s.engine.resolutionRegistry.GetRegion(gameID, width, height, name)
	if err != nil {
		log.Printf("Warning: region %q not found: %v", name, err)
		return image.Rectangle{}
	}
	
	return region
}

// Click sends a mouse click at the given position.
func (s *engineServices) Click(pos image.Point) {
	if s.engine.devMode {
		log.Printf("Dev mode: would click at (%d, %d)", pos.X, pos.Y)
		return
	}
	// TODO: Implement actual mouse click
}

// MoveMouse moves the mouse to the given position.
func (s *engineServices) MoveMouse(pos image.Point) {
	if s.engine.devMode {
		log.Printf("Dev mode: would move mouse to (%d, %d)", pos.X, pos.Y)
		return
	}
	// TODO: Implement actual mouse movement
}

// PressKey sends a key press.
func (s *engineServices) PressKey(key string) {
	if s.engine.devMode {
		log.Printf("Dev mode: would press key %q", key)
		return
	}
	// TODO: Implement actual key press
}

// TypeText types text with human-like delays.
func (s *engineServices) TypeText(text string) {
	if s.engine.devMode {
		log.Printf("Dev mode: would type %q", text)
		return
	}
	// TODO: Implement actual text typing
}

// Wait pauses for a human-like delay.
func (s *engineServices) Wait() {
	time.Sleep(time.Duration(100+rand.Intn(200)) * time.Millisecond)
}

// WaitMs pauses for a specific duration with randomization.
func (s *engineServices) WaitMs(baseMs int, varianceMs int) {
	variance := time.Duration(rand.Intn(varianceMs*2)-varianceMs) * time.Millisecond
	delay := time.Duration(baseMs)*time.Millisecond + variance
	time.Sleep(delay)
}

// Log logs a message associated with the plugin.
func (s *engineServices) Log(level string, msg string, args ...any) {
	prefix := fmt.Sprintf("[%s] %s", s.engine.gamePlugin.Info().ID, level)
	log.Printf(prefix+": "+msg, args...)
}

// Random number generator (TODO: use crypto/rand for better randomness)
var rand = struct {
	Intn func(int) int
}{
	Intn: func(n int) int {
		return int(time.Now().UnixNano()) % n
	},
}