"""
Base weather engine class.

All weather engines inherit from this base class to ensure consistent interface.
"""

import asyncio
import os
import random
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from soracli.renderer.ascii_renderer import ANSIRenderer


@dataclass
class Particle:
    """Represents a particle in the animation (raindrop, snowflake, etc.)."""
    x: float
    y: float
    char: str
    speed: float = 1.0
    color: str = ""
    drift: float = 0.0


@dataclass
class EngineConfig:
    """Configuration for weather engines."""
    width: int = 80
    height: int = 24
    intensity: float = 1.0
    theme: Dict[str, Any] = field(default_factory=dict)
    refresh_rate: float = 0.05
    sound_enabled: bool = False
    is_night: bool = False


class BaseWeatherEngine(ABC):
    """
    Abstract base class for all weather engines.
    
    Provides common functionality for particle-based animations
    and defines the interface that all engines must implement.
    """
    
    def __init__(self, config: Optional[EngineConfig] = None):
        self.config = config or EngineConfig()
        self.particles: List[Particle] = []
        self.renderer = ANSIRenderer()
        self.running = False
        self._update_terminal_size()
        
    def _update_terminal_size(self) -> None:
        """Update dimensions based on terminal size."""
        try:
            size = os.get_terminal_size()
            self.config.width = size.columns
            self.config.height = size.lines - 1  # Leave room for status
        except OSError:
            pass  # Keep default dimensions
    
    @abstractmethod
    def initialize_particles(self) -> None:
        """Initialize particles for the animation."""
        pass
    
    @abstractmethod
    def update_particles(self) -> None:
        """Update particle positions for the next frame."""
        pass
    
    @abstractmethod
    def get_particle_chars(self) -> List[str]:
        """Return the character set used for particles."""
        pass
    
    def spawn_particle(self) -> Particle:
        """Spawn a new particle at a random position."""
        chars = self.get_particle_chars()
        return Particle(
            x=random.uniform(0, self.config.width),
            y=0,
            char=random.choice(chars),
            speed=random.uniform(0.5, 1.5),
            drift=random.uniform(-0.1, 0.1)
        )
    
    def render_frame(self) -> str:
        """Render the current frame to a string."""
        # Create empty buffer
        buffer = [[' ' for _ in range(self.config.width)] 
                  for _ in range(self.config.height)]
        
        # Place particles in buffer
        for particle in self.particles:
            x = int(particle.x)
            y = int(particle.y)
            if 0 <= x < self.config.width and 0 <= y < self.config.height:
                buffer[y][x] = particle.char
        
        # Convert buffer to string with colors
        return self.renderer.render_buffer(
            buffer, 
            self.particles, 
            self.config.theme
        )
    
    async def run_loop(self) -> None:
        """Main animation loop."""
        self.running = True
        self.initialize_particles()
        
        try:
            while self.running:
                self._update_terminal_size()
                self.update_particles()
                frame = self.render_frame()
                
                # Clear screen and render
                print(self.renderer.clear_screen() + frame, end='', flush=True)
                
                await asyncio.sleep(self.config.refresh_rate)
        except asyncio.CancelledError:
            pass
        finally:
            self.running = False
            print(self.renderer.reset_terminal())
    
    def stop(self) -> None:
        """Stop the animation loop."""
        self.running = False
    
    def apply_theme(self, theme: Dict[str, Any]) -> None:
        """Apply a theme to the engine."""
        self.config.theme = theme
        
    def set_intensity(self, intensity: float) -> None:
        """Set the animation intensity (affects particle count/speed)."""
        self.config.intensity = max(0.1, min(2.0, intensity))


class PluginEngine(BaseWeatherEngine):
    """
    Base class for plugin engines.
    
    Allows users to create custom weather engines by extending this class.
    """
    
    plugin_name: str = "custom"
    plugin_version: str = "1.0.0"
    
    @classmethod
    def get_plugin_info(cls) -> Dict[str, str]:
        """Return plugin metadata."""
        return {
            "name": cls.plugin_name,
            "version": cls.plugin_version,
        }
