"""Reactive Store Generator.

Generates application state management stores with signals, computed properties,
and performance optimizations for modern Anvil apps.
"""

from pathlib import Path
from typing import Dict, List, Tuple, Any
from dataclasses import dataclass
import json

from ..models.project_context import ProjectContext
from ..validators.component_validator import ComponentValidator
from ..dependencies.container import FileSystem


@dataclass
class StoreConfig:
    """Configuration for a reactive store."""

    store_name: str
    signals: List[Dict[str, Any]]
    computed_properties: List[Dict[str, Any]]
    actions: List[Dict[str, Any]]
    subscriptions: List[Dict[str, Any]]
    persistence_config: Dict[str, Any]
    performance_config: Dict[str, Any]


@dataclass
class StoreSpec:
    """Complete specification for a reactive store."""

    config: StoreConfig
    store_class: str
    signal_declarations: str
    computed_properties_code: str
    actions_code: str
    subscriptions_code: str
    persistence_code: str
    performance_code: str


class ReactiveStoreGenerator:
    """Generate reactive stores with optimal performance."""

    def __init__(self, file_system: FileSystem) -> None:
        self.file_system: FileSystem = file_system
        self.validator: ComponentValidator = ComponentValidator()

    def create_reactive_store(
        self, config: StoreConfig, project_context: ProjectContext, output_path: str
    ) -> Tuple[bool, str]:
        """Create a complete reactive store."""
        try:
            # Generate store specification
            store_spec = self._create_store_spec(config, project_context)

            # Generate store class
            self._generate_store_class(store_spec, output_path)

            # Generate signal declarations
            self._generate_signal_declarations(store_spec, output_path)

            # Generate computed properties
            self._generate_computed_properties(store_spec, output_path)

            # Generate actions
            self._generate_actions(store_spec, output_path)

            # Generate subscriptions
            self._generate_subscriptions(store_spec, output_path)

            # Generate persistence layer
            self._generate_persistence_layer(store_spec, output_path)

            # Generate performance optimizations
            self._generate_performance_optimizations(store_spec, output_path)

            return True, f"Reactive store '{config.store_name}' created successfully"

        except Exception as e:
            return False, f"Error creating reactive store: {str(e)}"

    def _create_store_spec(
        self, config: StoreConfig, project_context: ProjectContext
    ) -> StoreSpec:
        """Create complete store specification."""

        # Generate store class name
        store_class = self._generate_store_class_name(config.store_name)

        # Create signal declarations
        signal_declarations = self._create_signal_declarations(config.signals)

        # Create computed properties code
        computed_properties_code = self._create_computed_properties_code(
            config.computed_properties
        )

        # Create actions code
        actions_code = self._create_actions_code(config.actions)

        # Create subscriptions code
        subscriptions_code = self._create_subscriptions_code(config.subscriptions)

        # Create persistence code
        persistence_code = self._create_persistence_code(config.persistence_config)

        # Create performance code
        performance_code = self._create_performance_code(config.performance_config)

        return StoreSpec(
            config=config,
            store_class=store_class,
            signal_declarations=signal_declarations,
            computed_properties_code=computed_properties_code,
            actions_code=actions_code,
            subscriptions_code=subscriptions_code,
            persistence_code=persistence_code,
            performance_code=performance_code,
        )

    def _generate_store_class_name(self, store_name: str) -> str:
        """Generate store class name from store name."""
        # Convert to PascalCase and add Store suffix
        return (
            "".join(
                word.capitalize()
                for word in store_name.replace("_", " ").replace("-", " ").split()
            )
            + "Store"
        )

    def _create_signal_declarations(self, signals: List[Dict[str, Any]]) -> str:
        """Create signal declarations code."""

        declarations = []
        for signal in signals:
            default_value = signal.get("default_value", "None")
            signal_type = signal.get("type", "signal")

            if signal_type == "signal":
                declarations.append(
                    f"        self.{signal['name']} = signal({default_value})"
                )
            elif signal_type == "computed":
                declarations.append(
                    f"        self.{signal['name']} = computed(self._compute_{signal['name']})"
                )

        return "\n".join(declarations)

    def _create_computed_properties_code(
        self, computed_props: List[Dict[str, Any]]
    ) -> str:
        """Create computed properties code."""

        properties = []
        for prop in computed_props:
            dependencies = ", ".join(prop.get("dependencies", []))
            compute_method = prop.get("compute_method", f"_compute_{prop['name']}")

            properties.append(f'''
    @computed
    def {prop["name"]}(self):
        """Computed {prop["name"]} property."""
        return {compute_method}({dependencies})''')

        return "\n".join(properties)

    def _create_actions_code(self, actions: List[Dict[str, Any]]) -> str:
        """Create actions code."""

        methods = []
        for action in actions:
            params = ", ".join(action.get("parameters", []))
            implementation = action.get("implementation", f"_{action['name']}")

            methods.append(f'''
    def {action["name"]}(self{f", {params}" if params else ""}):
        """{action.get("description", action["name"])} action."""
        return {implementation}({params})''')

        return "\n".join(methods)

    def _create_subscriptions_code(self, subscriptions: List[Dict[str, Any]]) -> str:
        """Create subscriptions code."""

        methods = []
        for sub in subscriptions:
            target = sub.get("target", "unknown")
            event = sub.get("event", "change")
            handler = sub.get("handler", f"_handle_{sub['name']}")

            methods.append(f'''
    def {handler}(self, value):
        """Handle {target} {event}."""
        # TODO: Implement {handler} logic
        pass''')

        return "\n".join(methods)

    def _create_persistence_code(self, persistence_config: Dict[str, Any]) -> str:
        """Create persistence layer code."""

        enabled = persistence_config.get("enabled", False)
        storage_type = persistence_config.get("storage_type", "local")
        ttl = persistence_config.get("ttl", 3600)

        if not enabled:
            return '''
    def _save_to_persistence(self, key: str, value):
        """Persistence disabled."""
        pass
    
    def _load_from_persistence(self, key: str, default=None):
        """Persistence disabled."""
        return default'''

        return f'''
    def _save_to_persistence(self, key: str, value):
        """Save to {storage_type} storage with TTL."""
        try:
            import anvil.server
            cache_key = f"{self._store_name}_{{key}}"
            anvil.server.cache.set(cache_key, value, expires_in={ttl})
        except Exception as e:
            print(f"Error saving to persistence: {{e}}")
    
    def _load_from_persistence(self, key: str, default=None):
        """Load from {storage_type} storage."""
        try:
            import anvil.server
            cache_key = f"{self._store_name}_{{key}}"
            return anvil.server.cache.get(cache_key, default=default)
        except Exception as e:
            print(f"Error loading from persistence: {{e}}")
            return default
    
    def _clear_persistence(self, pattern: str = None):
        """Clear from {storage_type} storage."""
        try:
            import anvil.server
            if pattern:
                cache_pattern = f"{self._store_name}_{{pattern}}"
                anvil.server.cache.remove_pattern(cache_pattern)
            else:
                anvil.server.cache.remove_pattern(f"{self._store_name}_*")
        except Exception as e:
            print(f"Error clearing persistence: {{e}}")'''

    def _create_performance_code(self, performance_config: Dict[str, Any]) -> str:
        """Create performance optimization code."""

        batching_enabled = performance_config.get("batching", {}).get("enabled", True)
        batch_size = performance_config.get("batching", {}).get("batch_size", 50)
        debounce_ms = performance_config.get("batching", {}).get("debounce_ms", 300)

        caching_enabled = performance_config.get("caching", {}).get("enabled", True)
        cache_ttl = performance_config.get("caching", {}).get("ttl", 300)

        return f'''
    def _batch_updates(self):
        """Batch multiple updates for performance."""
        if not hasattr(self, '_batch_queue'):
            self._batch_queue = []
            self._batch_timer = None
        
        if self._batch_timer:
            return
        
        # Process batch after debounce
        import anvil
        self._batch_timer = anvil.timer_with_callback(
            lambda: self._process_batch(),
            interval={debounce_ms}/1000
        )
    
    def _process_batch(self):
        """Process queued updates."""
        if not hasattr(self, '_batch_queue') or not self._batch_queue:
            return
        
        # Process all queued updates
        for update in self._batch_queue:
            update()
        
        # Clear queue and timer
        self._batch_queue.clear()
        self._batch_timer = None
    
    def _queue_update(self, update_func):
        """Queue update for batching."""
        if not hasattr(self, '_batch_queue'):
            self._batch_queue = []
        
        self._batch_queue.append(update_func)
        
        if len(self._batch_queue) >= {batch_size}:
            self._process_batch()
        elif not self._batch_timer:
            self._batch_updates()
    
    def _should_cache_result(self, key: str) -> bool:
        """Check if result should be cached."""
        return {str(caching_enabled).lower()}
    
    def _get_cache_key(self, key: str) -> str:
        """Get cache key with store prefix."""
        return f"{self._store_name}_{{key}}"
    
    def _get_cache_ttl(self) -> int:
        """Get cache TTL."""
        return {cache_ttl}'''

    def _generate_store_class(self, store_spec: StoreSpec, output_path: str) -> None:
        """Generate store class file."""

        base_path = Path(output_path)
        store_class_name = store_spec.store_class

        store_code = f'''"""{store_spec.config.store_name.title()} Store.

Reactive state management with signals, computed properties, and performance optimizations.
"""

import anvil
from anvil.reactive import signal, computed, reactive_class
from typing import Any, Dict, List, Optional

@reactive_class
class {store_class_name}:
    """Reactive store for {store_spec.config.store_name} state management."""
    
    def __init__(self):
        # Initialize performance tracking
        self._update_count = 0
        self._last_update = None
        self._performance_mode = "optimized"
        
        # Signal declarations
{store_spec.signal_declarations}
        
        # Initialize state
        self._initialize_state()
    
    def _initialize_state(self):
        """Initialize store state."""
        try:
            # Load from persistence
            self._load_from_persistence()
        except Exception as e:
            print(f"Error initializing {store_spec.config.store_name} store: {{e}}")
    
    @computed
    def is_dirty(self):
        """Computed dirty state."""
        return self._update_count > 0
    
    @computed
    def last_update(self):
        """Computed last update timestamp."""
        return self._last_update
    
    def mark_clean(self):
        """Mark store as clean."""
        self._update_count = 0
    
    def mark_dirty(self):
        """Mark store as dirty."""
        self._update_count += 1
        import datetime
        self._last_update = datetime.datetime.now()
    
{store_spec.computed_properties_code}
    
{store_spec.actions_code}
    
{store_spec.subscriptions_code}
    
{store_spec.persistence_code}
    
{store_spec.performance_code}
    
    def get_performance_stats(self) -> Dict[str, Any]:
        """Get performance statistics."""
        return {{
            "update_count": self._update_count,
            "last_update": self._last_update,
            "performance_mode": self._performance_mode,
            "signal_count": len([attr for attr in dir(self) if hasattr(getattr(self, attr), 'get')]),
            "computed_count": len([attr for attr in dir(self) if hasattr(getattr(self, attr), '_dependencies')])
        }}
    
    def optimize_performance(self, mode: str = "balanced"):
        """Optimize store performance."""
        self._performance_mode = mode
        
        if mode == "memory":
            # Optimize for memory usage
            pass
        elif mode == "speed":
            # Optimize for speed
            pass
        else:
            # Balanced optimization
            pass
    
    def cleanup(self):
        """Cleanup store resources."""
        # Clear timers, subscriptions, etc.
        if hasattr(self, '_batch_timer'):
            self._batch_timer = None
        
        # Save to persistence before cleanup
        self._save_to_persistence("state", self._get_state_snapshot())

# Global store instance
{store_spec.config.store_name}_store = {store_class_name}()
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_store.py"), store_code
        )

    def _generate_signal_declarations(
        self, store_spec: StoreSpec, output_path: str
    ) -> None:
        """Generate signal declarations file."""

        base_path = Path(output_path)

        signals_code = f'''"""Signal Declarations for {store_spec.config.store_name} Store."""

from anvil.reactive import signal, computed
from typing import Any, Dict, List

class {store_spec.config.store_name.title()}Signals:
    """Centralized signal declarations for {store_spec.config.store_name}."""
    
    def __init__(self):
        # Core signals
{store_spec.signal_declarations}
        
        # Utility signals
        self.loading = signal(False)
        self.error = signal(None)
        self.initialized = signal(False)
        
        # Performance signals
        self.performance_stats = signal({{}})
        self.optimization_mode = signal("balanced")

# Global signals instance
{store_spec.config.store_name}_signals = {store_spec.config.store_name.title()}Signals()
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_signals.py"), signals_code
        )

    def _generate_computed_properties(
        self, store_spec: StoreSpec, output_path: str
    ) -> None:
        """Generate computed properties file."""

        base_path = Path(output_path)

        computed_code = f'''"""Computed Properties for {store_spec.config.store_name} Store."""

from anvil.reactive import computed
from .{store_spec.config.store_name}_signals import {store_spec.config.store_name.title()}Signals

class {store_spec.config.store_name.title()}ComputedProperties:
    """Computed properties for {store_spec.config.store_name} store."""
    
    def __init__(self, signals):
        self.signals = signals
        
{store_spec.computed_properties_code}
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_computed.py"),
            computed_code,
        )

    def _generate_actions(self, store_spec: StoreSpec, output_path: str) -> None:
        """Generate actions file."""

        base_path = Path(output_path)

        actions_code = f'''"""Actions for {store_spec.config.store_name} Store."""

from typing import Any, Dict, List
from .{store_spec.config.store_name}_signals import {store_spec.config.store_name.title()}Signals

class {store_spec.config.store_name.title()}Actions:
    """Actions for {store_spec.config.store_name} store."""
    
    def __init__(self, signals, store):
        self.signals = signals
        self.store = store
        
{store_spec.actions_code}
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_actions.py"), actions_code
        )

    def _generate_subscriptions(self, store_spec: StoreSpec, output_path: str) -> None:
        """Generate subscriptions file."""

        base_path = Path(output_path)

        subscriptions_code = f'''"""Subscriptions for {store_spec.config.store_name} Store."""

from typing import Any, Dict, List
from .{store_spec.config.store_name}_signals import {store_spec.config.store_name.title()}Signals

class {store_spec.config.store_name.title()}Subscriptions:
    """Subscriptions for {store_spec.config.store_name} store."""
    
    def __init__(self, signals, store):
        self.signals = signals
        self.store = store
        
{store_spec.subscriptions_code}
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_subscriptions.py"),
            subscriptions_code,
        )

    def _generate_persistence_layer(
        self, store_spec: StoreSpec, output_path: str
    ) -> None:
        """Generate persistence layer file."""

        base_path = Path(output_path)

        persistence_code = f'''"""Persistence Layer for {store_spec.config.store_name} Store."""

from typing import Any, Dict, List, Optional
import anvil.server

class {store_spec.config.store_name.title()}Persistence:
    """Persistence layer for {store_spec.config.store_name} store."""
    
    def __init__(self, store_name: str):
        self.store_name = store_name
        
{store_spec.persistence_code}
    
    def get_state_key(self, key: str) -> str:
        """Get state key with store prefix."""
        return f"{{self.store_name}}_{{key}}"
    
    def serialize_state(self, state: Any) -> str:
        """Serialize state for storage."""
        import json
        return json.dumps(state, default=str)
    
    def deserialize_state(self, data: str) -> Any:
        """Deserialize state from storage."""
        import json
        try:
            return json.loads(data)
        except json.JSONDecodeError:
            return None
    
    def backup_state(self, state: Any):
        """Create backup of current state."""
        backup_key = self.get_state_key("backup")
        backup_data = self.serialize_state(state)
        anvil.server.cache.set(backup_key, backup_data, expires_in=86400)  # 24 hours
    
    def restore_state(self) -> Optional[Any]:
        """Restore state from backup."""
        backup_key = self.get_state_key("backup")
        backup_data = anvil.server.cache.get(backup_key)
        if backup_data:
            return self.deserialize_state(backup_data)
        return None
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_persistence.py"),
            persistence_code,
        )

    def _generate_performance_optimizations(
        self, store_spec: StoreSpec, output_path: str
    ) -> None:
        """Generate performance optimizations file."""

        base_path = Path(output_path)

        performance_code = f'''"""Performance Optimizations for {store_spec.config.store_name} Store."""

from typing import Any, Dict, List
import time
import threading
from collections import deque

class {store_spec.config.store_name.title()}PerformanceOptimizer:
    """Performance optimizations for {store_spec.config.store_name} store."""
    
    def __init__(self):
        self.update_queue = deque(maxlen=100)
        self.batch_size = 50
        self.debounce_time = 0.3  # 300ms
        self.last_batch_time = 0
        self.performance_stats = {{
            "update_count": 0,
            "batch_count": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "average_update_time": 0.0
        }}
        
{store_spec.performance_code}
    
    def queue_update(self, update_func):
        """Queue update for batching."""
        self.update_queue.append(update_func)
        self._schedule_batch()
    
    def _schedule_batch(self):
        """Schedule batch processing."""
        current_time = time.time()
        
        if current_time - self.last_batch_time >= self.debounce_time:
            self._process_batch()
            self.last_batch_time = current_time
        else:
            # Schedule for later
            import threading
            timer = threading.Timer(
                self.debounce_time - (current_time - self.last_batch_time),
                self._process_batch
            )
            timer.start()
    
    def _process_batch(self):
        """Process queued updates."""
        if not self.update_queue:
            return
        
        start_time = time.time()
        batch_size = min(len(self.update_queue), self.batch_size)
        
        # Process batch
        for _ in range(batch_size):
            if self.update_queue:
                update_func = self.update_queue.popleft()
                update_func()
        
        # Update stats
        end_time = time.time()
        self.performance_stats["update_count"] += batch_size
        self.performance_stats["batch_count"] += 1
        self.performance_stats["average_update_time"] = (
            (self.performance_stats["average_update_time"] * self.performance_stats["update_count"] + 
             (end_time - start_time) * batch_size
        ) / (self.performance_stats["update_count"] + batch_size)
        )
    
    def get_stats(self) -> Dict[str, Any]:
        """Get performance statistics."""
        return self.performance_stats.copy()
    
    def reset_stats(self):
        """Reset performance statistics."""
        self.performance_stats = {{
            "update_count": 0,
            "batch_count": 0,
            "cache_hits": 0,
            "cache_misses": 0,
            "average_update_time": 0.0
        }}
'''

        self.file_system.write_text(
            str(base_path / f"{store_spec.config.store_name}_performance.py"),
            performance_code,
        )

    def _build_yaml_from_dict(self, data: Dict[str, Any]) -> str:
        """Build YAML string from dictionary with proper formatting."""
        import yaml

        return yaml.dump(data, default_flow_style=False, indent=2, sort_keys=False)
