"""
Clear weather engine.

Creates ambient animations for clear weather - stars at night, clouds during day.
"""

import math
import random
from typing import List, Optional

from soracli.engines.base import BaseWeatherEngine, Particle, EngineConfig


class ClearEngine(BaseWeatherEngine):
    """
    Clear weather simulation engine.
    
    Shows twinkling stars at night or drifting clouds during the day.
    """
    
    # Star characters
    STAR_CHARS = ['·', '∗', '✦', '★', '☆', '*', '✧', '⋆', '✶']
    STAR_CHARS_BRIGHT = ['★', '✦', '✶', '✹', '✺']
    
    # Cloud characters
    CLOUD_CHARS = ['☁', '○', '◌', '◯', '⬭']
    CLOUD_BODY = ['░', '▒', '◌']
    
    def __init__(self, config: EngineConfig = None):
        super().__init__(config)
        self.time = 0.0
        self.clouds: List[dict] = []
        self.star_twinkle_phase: dict = {}
        
    def get_particle_chars(self) -> List[str]:
        """Return character set based on time of day."""
        if self.config.is_night:
            return self.STAR_CHARS
        return self.CLOUD_CHARS
    
    def initialize_particles(self) -> None:
        """Initialize stars or clouds based on time of day."""
        self.particles = []
        
        if self.config.is_night:
            self._initialize_stars()
        else:
            self._initialize_clouds()
    
    def _initialize_stars(self) -> None:
        """Initialize star field."""
        # Calculate star density based on intensity
        star_count = int(
            self.config.width * self.config.height * 0.01 * self.config.intensity
        )
        
        for _ in range(star_count):
            particle = Particle(
                x=random.uniform(0, self.config.width),
                y=random.uniform(0, self.config.height),
                char=random.choice(self.STAR_CHARS),
                speed=0,  # Stars don't move
                color=self._get_star_color()
            )
            self.particles.append(particle)
            
            # Set up twinkle phase for each star
            star_id = id(particle)
            self.star_twinkle_phase[star_id] = random.uniform(0, math.pi * 2)
    
    def _get_star_color(self) -> str:
        """Get color for a star (varies slightly)."""
        colors = ['white', 'bright_white', 'yellow', 'cyan']
        theme_colors = self.config.theme.get('star_colors', colors)
        return random.choice(theme_colors)
    
    def _initialize_clouds(self) -> None:
        """Initialize cloud formations."""
        cloud_count = int(3 * self.config.intensity)
        
        for _ in range(cloud_count):
            self.clouds.append(self._generate_cloud())
    
    def _generate_cloud(self, x: Optional[float] = None) -> dict:
        """Generate a cloud formation."""
        cloud_width = random.randint(8, 20)
        cloud_height = random.randint(2, 4)
        
        return {
            'x': x if x is not None else random.uniform(-cloud_width, self.config.width),
            'y': random.uniform(1, self.config.height // 3),
            'width': cloud_width,
            'height': cloud_height,
            'speed': random.uniform(0.05, 0.15),
            'shape': self._generate_cloud_shape(cloud_width, cloud_height),
        }
    
    def _generate_cloud_shape(self, width: int, height: int) -> List[List[str]]:
        """Generate a cloud shape pattern."""
        shape = []
        
        for y in range(height):
            row = []
            for x in range(width):
                # Create rounded cloud shape
                center_x = width / 2
                center_y = height / 2
                
                # Distance from center (elliptical)
                dx = (x - center_x) / (width / 2)
                dy = (y - center_y) / (height / 2)
                dist = math.sqrt(dx * dx + dy * dy)
                
                # Add some randomness to create fluffy edges
                dist += random.uniform(-0.2, 0.2)
                
                if dist < 0.5:
                    row.append(random.choice(['▓', '█']))
                elif dist < 0.8:
                    row.append(random.choice(['░', '▒']))
                elif dist < 1.0:
                    row.append(random.choice([' ', '░', ' ']))
                else:
                    row.append(' ')
            shape.append(row)
        
        return shape
    
    def update_particles(self) -> None:
        """Update stars (twinkle) or clouds (drift)."""
        self.time += 0.1
        
        if self.config.is_night:
            self._update_stars()
        else:
            self._update_clouds()
    
    def _update_stars(self) -> None:
        """Update star twinkling effect."""
        for particle in self.particles:
            star_id = id(particle)
            phase = self.star_twinkle_phase.get(star_id, 0)
            
            # Calculate brightness based on sine wave
            brightness = (math.sin(self.time * 2 + phase) + 1) / 2
            
            # Change character based on brightness
            if brightness > 0.8:
                particle.char = random.choice(self.STAR_CHARS_BRIGHT)
            elif brightness > 0.5:
                particle.char = random.choice(self.STAR_CHARS)
            elif brightness > 0.2:
                particle.char = random.choice(['·', '∗', '*'])
            else:
                particle.char = ' '  # Star dims out momentarily
    
    def _update_clouds(self) -> None:
        """Update cloud positions."""
        new_clouds = []
        
        for cloud in self.clouds:
            cloud['x'] += cloud['speed']
            
            # If cloud moved off screen, respawn from left
            if cloud['x'] > self.config.width:
                new_clouds.append(self._generate_cloud(x=-cloud['width']))
            else:
                new_clouds.append(cloud)
        
        self.clouds = new_clouds
    
    def render_frame(self) -> str:
        """Render the clear weather frame."""
        # Create empty buffer
        buffer = [[' ' for _ in range(self.config.width)] 
                  for _ in range(self.config.height)]
        
        if self.config.is_night:
            # Draw stars
            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
        else:
            # Draw clouds
            for cloud in self.clouds:
                self._draw_cloud(buffer, cloud)
        
        return self.renderer.render_buffer(
            buffer, 
            self.particles, 
            self.config.theme
        )
    
    def _draw_cloud(self, buffer: List[List[str]], cloud: dict) -> None:
        """Draw a cloud onto the buffer."""
        start_x = int(cloud['x'])
        start_y = int(cloud['y'])
        
        for dy, row in enumerate(cloud['shape']):
            for dx, char in enumerate(row):
                x = start_x + dx
                y = start_y + dy
                
                if 0 <= x < self.config.width and 0 <= y < self.config.height:
                    if char != ' ':
                        buffer[y][x] = char
    
    def toggle_day_night(self) -> None:
        """Toggle between day and night mode."""
        self.config.is_night = not self.config.is_night
        self.initialize_particles()
    
    def set_time_of_day(self, is_night: bool) -> None:
        """Explicitly set the time of day."""
        if self.config.is_night != is_night:
            self.config.is_night = is_night
            self.initialize_particles()
