"""
OpenWork Configuration Loader
=============================
Loads and validates OpenWork integration configuration.

Usage:
    from core.openwork_config_loader import OpenWorkConfig

    config = OpenWorkConfig.load()
    print(config.voice.stt.model)

Author: Genesis System
Version: 1.0.0
"""

import os
import json
import logging
from pathlib import Path
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, field

# Configure logging
logger = logging.getLogger(__name__)

# Default config path
GENESIS_ROOT = Path(__file__).parent.parent
DEFAULT_CONFIG_PATH = GENESIS_ROOT / "config" / "openwork_config.json"


@dataclass
class STTConfig:
    """Speech-to-text configuration."""
    engine: str = "whisper"
    model: str = "medium.en"
    device: str = "auto"
    compute_type: str = "auto"
    language: str = "en"
    vad_filter: bool = True
    vad_silence_ms: int = 500


@dataclass
class TTSVoiceConfig:
    """TTS voice-specific configuration."""
    model: Optional[str] = None
    voice_id: Optional[str] = None
    model_id: Optional[str] = None
    style: Optional[str] = None
    stability: Optional[float] = None
    similarity_boost: Optional[float] = None


@dataclass
class TTSConfig:
    """Text-to-speech configuration."""
    primary_backend: str = "gemini"
    fallback_chain: List[str] = field(default_factory=lambda: ["elevenlabs", "local"])
    daily_budget: float = 5.0
    default_style: str = "professional"
    voices: Dict[str, TTSVoiceConfig] = field(default_factory=dict)


@dataclass
class DictationConfig:
    """Voice dictation configuration."""
    hotkey: str = "ctrl+alt+v"
    typing_delay: float = 0.005
    clipboard_enabled: bool = True


@dataclass
class KinanChannelConfig:
    """Kinan voice channel configuration."""
    websocket_port: int = 8766
    enable_wake_word: bool = False
    wake_word: str = "hey aiva"


@dataclass
class VoiceConfig:
    """Voice configuration."""
    stt: STTConfig = field(default_factory=STTConfig)
    tts: TTSConfig = field(default_factory=TTSConfig)
    dictation: DictationConfig = field(default_factory=DictationConfig)
    kinan_channel: KinanChannelConfig = field(default_factory=KinanChannelConfig)


@dataclass
class ConnectionConfig:
    """OpenWork connection configuration."""
    mode: str = "server"
    server_host: str = "0.0.0.0"
    server_port: int = 8767
    client_url: str = "ws://localhost:8767"
    reconnect_delay: int = 5
    max_reconnect_attempts: int = 10
    heartbeat_interval: int = 30


@dataclass
class OpenWorkAppConfig:
    """OpenWork desktop app configuration."""
    implementation: str = "electron"
    connection: ConnectionConfig = field(default_factory=ConnectionConfig)
    desktop_path: Optional[str] = None
    auto_start: bool = False


@dataclass
class ApprovalConfig:
    """Action approval configuration."""
    auto_approve_low_risk: bool = True
    auto_approve_timeout_hours: int = 48
    low_risk_actions: List[str] = field(default_factory=lambda: ["notification", "clipboard", "file_read"])
    high_risk_actions: List[str] = field(default_factory=lambda: ["file_delete", "system_command"])


@dataclass
class RoutingConfig:
    """Action routing configuration."""
    default_priority: int = 5
    max_retries: int = 3
    retry_delay_base: int = 2


@dataclass
class RateLimitsConfig:
    """Rate limiting configuration."""
    actions_per_minute: int = 30
    file_operations_per_minute: int = 10
    browser_tasks_per_minute: int = 5


@dataclass
class ActionsConfig:
    """Actions configuration."""
    approval: ApprovalConfig = field(default_factory=ApprovalConfig)
    routing: RoutingConfig = field(default_factory=RoutingConfig)
    rate_limits: RateLimitsConfig = field(default_factory=RateLimitsConfig)


@dataclass
class IntegrationConfig:
    """Integration configuration."""
    rwl_enabled: bool = True
    rwl_task_queue: str = "redis"
    rwl_redis_key: str = "genesis:openwork_tasks"
    kinan_liaison_enabled: bool = True
    kinan_liaison_db_path: str = "data/kinan_liaison.db"
    websocket_server_enabled: bool = True
    websocket_server_port: int = 8765


@dataclass
class MonitoringConfig:
    """Monitoring configuration."""
    health_check_interval: int = 60
    metrics_enabled: bool = True
    redis_health_key: str = "genesis:health:openwork"
    log_level: str = "INFO"


@dataclass
class SecurityConfig:
    """Security configuration."""
    allowed_file_paths: List[str] = field(default_factory=list)
    blocked_file_patterns: List[str] = field(default_factory=list)
    allowed_urls: List[str] = field(default_factory=list)
    blocked_urls: List[str] = field(default_factory=list)


@dataclass
class OpenWorkConfig:
    """Complete OpenWork configuration."""
    version: str = "1.0.0"
    openwork: OpenWorkAppConfig = field(default_factory=OpenWorkAppConfig)
    voice: VoiceConfig = field(default_factory=VoiceConfig)
    actions: ActionsConfig = field(default_factory=ActionsConfig)
    integration: IntegrationConfig = field(default_factory=IntegrationConfig)
    monitoring: MonitoringConfig = field(default_factory=MonitoringConfig)
    security: SecurityConfig = field(default_factory=SecurityConfig)

    @classmethod
    def load(cls, config_path: Optional[str] = None) -> "OpenWorkConfig":
        """Load configuration from file."""
        path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH

        if not path.exists():
            logger.warning(f"Config file not found: {path}, using defaults")
            return cls()

        try:
            with open(path) as f:
                data = json.load(f)
            return cls._from_dict(data)
        except Exception as e:
            logger.error(f"Failed to load config: {e}")
            return cls()

    @classmethod
    def _from_dict(cls, data: Dict[str, Any]) -> "OpenWorkConfig":
        """Create config from dictionary."""
        config = cls()

        config.version = data.get("version", "1.0.0")

        # OpenWork config
        ow = data.get("openwork", {})
        config.openwork.implementation = ow.get("implementation", "electron")

        conn = ow.get("connection", {})
        config.openwork.connection.mode = conn.get("mode", "server")
        server = conn.get("server", {})
        config.openwork.connection.server_host = server.get("host", "0.0.0.0")
        config.openwork.connection.server_port = server.get("port", 8767)
        client = conn.get("client", {})
        config.openwork.connection.client_url = client.get("url", "ws://localhost:8767")
        config.openwork.connection.reconnect_delay = client.get("reconnect_delay", 5)
        config.openwork.connection.max_reconnect_attempts = client.get("max_reconnect_attempts", 10)
        config.openwork.connection.heartbeat_interval = conn.get("heartbeat_interval", 30)

        desktop = ow.get("desktop_app", {})
        config.openwork.desktop_path = desktop.get("path")
        config.openwork.auto_start = desktop.get("auto_start", False)

        # Voice config
        voice = data.get("voice", {})

        stt = voice.get("stt", {})
        config.voice.stt = STTConfig(
            engine=stt.get("engine", "whisper"),
            model=stt.get("model", "medium.en"),
            device=stt.get("device", "auto"),
            compute_type=stt.get("compute_type", "auto"),
            language=stt.get("language", "en"),
            vad_filter=stt.get("vad_filter", True),
            vad_silence_ms=stt.get("vad_silence_ms", 500)
        )

        tts = voice.get("tts", {})
        config.voice.tts = TTSConfig(
            primary_backend=tts.get("primary_backend", "gemini"),
            fallback_chain=tts.get("fallback_chain", ["elevenlabs", "local"]),
            daily_budget=tts.get("daily_budget", 5.0),
            default_style=tts.get("default_style", "professional")
        )

        # Parse TTS voices
        for voice_name, voice_config in tts.get("voices", {}).items():
            config.voice.tts.voices[voice_name] = TTSVoiceConfig(**voice_config)

        dictation = voice.get("dictation", {})
        config.voice.dictation = DictationConfig(
            hotkey=dictation.get("hotkey", "ctrl+alt+v"),
            typing_delay=dictation.get("typing_delay", 0.005),
            clipboard_enabled=dictation.get("clipboard_enabled", True)
        )

        kinan = voice.get("kinan_channel", {})
        config.voice.kinan_channel = KinanChannelConfig(
            websocket_port=kinan.get("websocket_port", 8766),
            enable_wake_word=kinan.get("enable_wake_word", False),
            wake_word=kinan.get("wake_word", "hey aiva")
        )

        # Actions config
        actions = data.get("actions", {})

        approval = actions.get("approval", {})
        config.actions.approval = ApprovalConfig(
            auto_approve_low_risk=approval.get("auto_approve_low_risk", True),
            auto_approve_timeout_hours=approval.get("auto_approve_timeout_hours", 48),
            low_risk_actions=approval.get("low_risk_actions", []),
            high_risk_actions=approval.get("high_risk_actions", [])
        )

        routing = actions.get("routing", {})
        config.actions.routing = RoutingConfig(
            default_priority=routing.get("default_priority", 5),
            max_retries=routing.get("max_retries", 3),
            retry_delay_base=routing.get("retry_delay_base", 2)
        )

        rate_limits = actions.get("rate_limits", {})
        config.actions.rate_limits = RateLimitsConfig(
            actions_per_minute=rate_limits.get("actions_per_minute", 30),
            file_operations_per_minute=rate_limits.get("file_operations_per_minute", 10),
            browser_tasks_per_minute=rate_limits.get("browser_tasks_per_minute", 5)
        )

        # Integration config
        integration = data.get("integration", {})
        rwl = integration.get("rwl", {})
        liaison = integration.get("kinan_liaison", {})
        ws = integration.get("websocket_server", {})

        config.integration = IntegrationConfig(
            rwl_enabled=rwl.get("enabled", True),
            rwl_task_queue=rwl.get("task_queue", "redis"),
            rwl_redis_key=rwl.get("redis_key", "genesis:openwork_tasks"),
            kinan_liaison_enabled=liaison.get("enabled", True),
            kinan_liaison_db_path=liaison.get("db_path", "data/kinan_liaison.db"),
            websocket_server_enabled=ws.get("enabled", True),
            websocket_server_port=ws.get("port", 8765)
        )

        # Monitoring config
        monitoring = data.get("monitoring", {})
        config.monitoring = MonitoringConfig(
            health_check_interval=monitoring.get("health_check_interval", 60),
            metrics_enabled=monitoring.get("metrics_enabled", True),
            redis_health_key=monitoring.get("redis_health_key", "genesis:health:openwork"),
            log_level=monitoring.get("log_level", "INFO")
        )

        # Security config
        security = data.get("security", {})
        config.security = SecurityConfig(
            allowed_file_paths=security.get("allowed_file_paths", []),
            blocked_file_patterns=security.get("blocked_file_patterns", []),
            allowed_urls=security.get("allowed_urls", []),
            blocked_urls=security.get("blocked_urls", [])
        )

        return config

    def validate(self) -> List[str]:
        """Validate configuration and return list of errors."""
        errors = []

        # Validate voice settings
        valid_stt_engines = ["whisper", "vosk", "google", "azure"]
        if self.voice.stt.engine not in valid_stt_engines:
            errors.append(f"Invalid STT engine: {self.voice.stt.engine}")

        valid_tts_backends = ["gemini", "elevenlabs", "openai", "local"]
        if self.voice.tts.primary_backend not in valid_tts_backends:
            errors.append(f"Invalid TTS backend: {self.voice.tts.primary_backend}")

        # Validate connection settings
        if self.openwork.connection.mode not in ["server", "client"]:
            errors.append(f"Invalid connection mode: {self.openwork.connection.mode}")

        if not 1024 <= self.openwork.connection.server_port <= 65535:
            errors.append(f"Invalid server port: {self.openwork.connection.server_port}")

        # Validate rate limits
        if self.actions.rate_limits.actions_per_minute < 1:
            errors.append("actions_per_minute must be at least 1")

        # Validate security paths
        for path in self.security.allowed_file_paths:
            if not Path(path).is_absolute() and not path.startswith("/"):
                errors.append(f"File path should be absolute: {path}")

        return errors

    def to_dict(self) -> Dict[str, Any]:
        """Convert config to dictionary."""
        return {
            "version": self.version,
            "openwork": {
                "implementation": self.openwork.implementation,
                "connection": {
                    "mode": self.openwork.connection.mode,
                    "server": {
                        "host": self.openwork.connection.server_host,
                        "port": self.openwork.connection.server_port
                    },
                    "client": {
                        "url": self.openwork.connection.client_url,
                        "reconnect_delay": self.openwork.connection.reconnect_delay,
                        "max_reconnect_attempts": self.openwork.connection.max_reconnect_attempts
                    },
                    "heartbeat_interval": self.openwork.connection.heartbeat_interval
                }
            },
            "voice": {
                "stt": {
                    "engine": self.voice.stt.engine,
                    "model": self.voice.stt.model,
                    "device": self.voice.stt.device
                },
                "tts": {
                    "primary_backend": self.voice.tts.primary_backend,
                    "fallback_chain": self.voice.tts.fallback_chain,
                    "daily_budget": self.voice.tts.daily_budget
                },
                "dictation": {
                    "hotkey": self.voice.dictation.hotkey
                }
            }
        }

    def save(self, config_path: Optional[str] = None):
        """Save configuration to file."""
        path = Path(config_path) if config_path else DEFAULT_CONFIG_PATH
        path.parent.mkdir(parents=True, exist_ok=True)

        with open(path, "w") as f:
            json.dump(self.to_dict(), f, indent=2)

        logger.info(f"Config saved to: {path}")


# Singleton instance
_config_instance: Optional[OpenWorkConfig] = None


def get_config() -> OpenWorkConfig:
    """Get singleton config instance."""
    global _config_instance
    if _config_instance is None:
        _config_instance = OpenWorkConfig.load()
    return _config_instance


def reload_config() -> OpenWorkConfig:
    """Reload config from file."""
    global _config_instance
    _config_instance = OpenWorkConfig.load()
    return _config_instance


# CLI for testing
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="OpenWork Config Loader")
    parser.add_argument("--validate", action="store_true", help="Validate config")
    parser.add_argument("--show", action="store_true", help="Show config")
    parser.add_argument("--path", type=str, help="Config file path")
    args = parser.parse_args()

    config = OpenWorkConfig.load(args.path)

    if args.validate:
        errors = config.validate()
        if errors:
            print("Validation errors:")
            for error in errors:
                print(f"  - {error}")
        else:
            print("Config is valid!")

    if args.show:
        print(json.dumps(config.to_dict(), indent=2))
