"""
Snow weather engine.

Creates animated falling snowflakes with gentle drifting motion.
"""

import math
import random
from typing import List

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


class SnowEngine(BaseWeatherEngine):
    """
    Snow simulation engine.
    
    Creates falling snowflakes with realistic drifting and accumulation effects.
    """
    
    # Snowflake characters (various sizes and styles)
    SNOW_CHARS_SMALL = ['·', '•', '°', '∘']
    SNOW_CHARS_MEDIUM = ['❄', '❅', '❆', '✱', '*']
    SNOW_CHARS_LARGE = ['❉', '✳', '✴', '✶', '✷']
    
    def __init__(self, config: EngineConfig = None):
        super().__init__(config)
        self.time = 0.0
        self.wind_phase = random.uniform(0, math.pi * 2)
        self.accumulation: List[int] = []  # Snow piled at bottom
        
    def get_particle_chars(self) -> List[str]:
        """Return snowflake character set based on intensity."""
        if self.config.intensity > 1.5:
            return self.SNOW_CHARS_LARGE + self.SNOW_CHARS_MEDIUM
        elif self.config.intensity > 1.0:
            return self.SNOW_CHARS_MEDIUM + self.SNOW_CHARS_SMALL
        return self.SNOW_CHARS_SMALL
    
    def initialize_particles(self) -> None:
        """Initialize snowflakes across the screen."""
        particle_count = int(
            self.config.width * self.config.height * 0.015 * self.config.intensity
        )
        self.particles = []
        self.accumulation = [0] * self.config.width
        
        for _ in range(particle_count):
            self.particles.append(self._spawn_snowflake())
    
    def _spawn_snowflake(self, from_top: bool = False) -> Particle:
        """Spawn a new snowflake."""
        chars = self.get_particle_chars()
        size = random.choice(['small', 'medium', 'large'])
        
        if size == 'small':
            char = random.choice(self.SNOW_CHARS_SMALL)
            speed = random.uniform(0.1, 0.3)
        elif size == 'medium':
            char = random.choice(self.SNOW_CHARS_MEDIUM)
            speed = random.uniform(0.2, 0.4)
        else:
            char = random.choice(self.SNOW_CHARS_LARGE)
            speed = random.uniform(0.3, 0.5)
        
        return Particle(
            x=random.uniform(0, self.config.width),
            y=0 if from_top else random.uniform(0, self.config.height),
            char=char,
            speed=speed * self.config.intensity,
            drift=random.uniform(-0.3, 0.3),
            color=self._get_snow_color()
        )
    
    def _get_snow_color(self) -> str:
        """Get color based on theme or default."""
        theme_colors = self.config.theme.get('snow_colors', ['white', 'bright_white'])
        return random.choice(theme_colors) if theme_colors else 'white'
    
    def _calculate_wind_drift(self) -> float:
        """Calculate current wind drift using sinusoidal pattern."""
        return math.sin(self.time * 0.5 + self.wind_phase) * 0.2
    
    def update_particles(self) -> None:
        """Update all snowflake particles."""
        self.time += 0.1
        base_wind = self._calculate_wind_drift()
        
        new_particles = []
        
        for particle in self.particles:
            # Update position with swaying motion
            particle.y += particle.speed
            
            # Sinusoidal horizontal drift for realistic snow movement
            sway = math.sin(self.time + particle.x * 0.1) * 0.15
            particle.x += particle.drift + base_wind + sway
            
            # Ground level considers accumulation
            x_idx = int(particle.x) % self.config.width
            ground_level = self.config.height - 1 - self.accumulation[x_idx]
            
            # Check if particle hit the ground/accumulation
            if particle.y >= ground_level:
                # Add to accumulation (very slowly)
                if random.random() < 0.001 * self.config.intensity:
                    if self.accumulation[x_idx] < self.config.height // 4:
                        self.accumulation[x_idx] += 1
                
                # Respawn from top
                new_particles.append(self._spawn_snowflake(from_top=True))
            elif particle.x < 0:
                particle.x = self.config.width - 1
                new_particles.append(particle)
            elif particle.x >= self.config.width:
                particle.x = 0
                new_particles.append(particle)
            else:
                new_particles.append(particle)
        
        self.particles = new_particles
    
    def render_frame(self) -> str:
        """Render the current frame including snow accumulation."""
        # Create empty buffer
        buffer = [[' ' for _ in range(self.config.width)] 
                  for _ in range(self.config.height)]
        
        # Draw accumulation at bottom
        for x, height in enumerate(self.accumulation):
            for h in range(height):
                y = self.config.height - 1 - h
                if 0 <= y < self.config.height:
                    buffer[y][x] = random.choice(['▓', '░', '█'])
        
        # 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
        )
    
    def reset_accumulation(self) -> None:
        """Reset snow accumulation."""
        self.accumulation = [0] * self.config.width
