"""
Server Template Configuration Manager

Manages per-server template configurations with template-based initialization,
auto-detection, customization, and sync capabilities.
"""

import copy
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Optional

import yaml

from navig import console_helper as ch
from navig.config import ConfigManager, get_config_manager
from navig.template_manager import TemplateManager


class ServerTemplateManager:
    """
    Manages template configurations for individual servers.
    
    Implements hybrid storage approach:
    - Server YAML tracks template state (enabled, version, detection info)
    - Separate YAML files store customizations (only when needed)
    - Templates serve as defaults until overridden
    
    Directory Structure:
        ~/.navig/apps/
        ├── production.yaml              # Server config with template state
        └── production/                  # Server-specific data
            └── templates/                  # Template customizations
                ├── n8n.yaml             # Only exists if customized
                ├── gitea.yaml
                └── hestiacp.yaml
    """

    def __init__(self, config_manager: Optional[ConfigManager] = None,
                 template_manager: Optional[TemplateManager] = None):
        """
        Initialize ServerTemplateManager.
        
        Args:
            config_manager: ConfigManager instance (creates new if None)
            template_manager: TemplateManager instance (creates new if None)
        """
        self.config_manager = config_manager or get_config_manager()
        self.template_manager = template_manager or TemplateManager()

        # Discover available templates
        self.template_manager.discover_templates()

    def _get_server_template_dir(self, server_name: str) -> Path:
        """Get the template directory for a specific server."""
        server_dir = self.config_manager.apps_dir / server_name
        template_dir = server_dir / "templates"
        return template_dir

    def _ensure_server_template_dir(self, server_name: str):
        """Create server template directory if it doesn't exist."""
        template_dir = self._get_server_template_dir(server_name)
        template_dir.mkdir(parents=True, exist_ok=True)

    def initialize_templates_from_detection(
        self,
        server_name: str,
        detected_templates: Dict[str, Dict[str, Any]]
    ) -> Dict[str, bool]:
        """
        Initialize template configurations from auto-detection results.
        
        Args:
            server_name: Name of the server
            detected_templates: Dict from ServerDiscovery.discover_templates()
                Format: {'template_name': {'detected': True, 'version': '1.0.0', ...}}
        
        Returns:
            Dict mapping template names to initialization success status
        """
        ch.step(f"Initializing templates for server '{server_name}'...")

        results = {}

        # Load server config
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return results

        # Initialize templates section if not exists
        if 'templates' not in server_config:
            server_config['templates'] = {}

        # Process each detected template
        for template_name, detection_info in detected_templates.items():
            # Check if template template exists
            template = self.template_manager.get_template(template_name)
            if not template:
                ch.warning(f"No template found for detected template '{template_name}'")
                results[template_name] = False
                continue

            # Initialize template config in server YAML
            server_config['templates'][template_name] = {
                'enabled': True,  # Auto-enable detected templates
                'template_version': template.metadata.get('version', '1.0.0'),
                'last_synced': datetime.now().isoformat(),
                'auto_detected': True,
                'customized': False,
                'detection_info': {
                    'version': detection_info.get('version'),
                    'paths': detection_info.get('paths', {}),
                    'services': detection_info.get('services', []),
                    'ports': detection_info.get('ports', [])
                }
            }

            results[template_name] = True
            ch.success(f"Initialized template '{template_name}'")

        # Save updated server config
        self.config_manager.save_server_config(server_name, server_config)

        return results

    def initialize_template_manually(
        self,
        server_name: str,
        template_name: str,
        enabled: bool = False
    ) -> bool:
        """
        Manually initialize an template for a server (not auto-detected).
        
        Args:
            server_name: Name of the server
            template_name: Name of the template to initialize
            enabled: Whether to enable the template immediately
        
        Returns:
            True if successful, False otherwise
        """
        # Check if template template exists
        template = self.template_manager.get_template(template_name)
        if not template:
            ch.error(f"Template template '{template_name}' not found")
            return False

        # Load server config
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return False

        # Initialize templates section if not exists
        if 'templates' not in server_config:
            server_config['templates'] = {}

        # Check if already initialized
        if template_name in server_config['templates']:
            ch.warning(f"Template '{template_name}' already initialized for server '{server_name}'")
            return True

        # Initialize template config
        server_config['templates'][template_name] = {
            'enabled': enabled,
            'template_version': template.metadata.get('version', '1.0.0'),
            'last_synced': datetime.now().isoformat(),
            'auto_detected': False,
            'customized': False
        }

        # Save updated server config
        self.config_manager.save_server_config(server_name, server_config)

        ch.success(f"Initialized template '{template_name}' for server '{server_name}'")
        return True

    def get_template_config(
        self,
        server_name: str,
        template_name: str,
        include_template: bool = True
    ) -> Optional[Dict[str, Any]]:
        """
        Get merged template configuration for a server.
        
        Merge order:
        1. Start with template template
        2. Apply detection info (if auto-detected)
        3. Apply custom overrides (if customization file exists)
        
        Args:
            server_name: Name of the server
            template_name: Name of the template
            include_template: Whether to merge with template (default: True)
        
        Returns:
            Merged configuration dict or None if not initialized
        """
        # Load server config
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return None

        # Check if template is initialized for this server
        templates = server_config.get('templates', {})
        if template_name not in templates:
            return None

        template_state = templates[template_name]

        # Start with template if requested
        if include_template:
            template = self.template_manager.get_template(template_name)
            if not template:
                ch.error(f"Template template '{template_name}' not found")
                return None

            # Deep copy template metadata (paths, services, env_vars, etc.)
            merged_config = {
                'paths': copy.deepcopy(template.get_paths()),
                'services': copy.deepcopy(template.get_services()),
                'env_vars': copy.deepcopy(template.get_env_vars()),
                'commands': copy.deepcopy(template.get_commands()),
            }

            # Include API config if present
            if 'api' in template.metadata:
                merged_config['api'] = copy.deepcopy(template.metadata['api'])
        else:
            merged_config = {}

        # Apply detection info overrides
        if template_state.get('auto_detected') and 'detection_info' in template_state:
            detection = template_state['detection_info']

            # Merge detected paths (override template)
            if detection.get('paths'):
                if 'paths' not in merged_config:
                    merged_config['paths'] = {}
                merged_config['paths'].update(detection['paths'])

        # Apply custom overrides from file (check YAML first, then JSON for backwards compat)
        yaml_config_file = self._get_server_template_dir(server_name) / f"{template_name}.yaml"
        json_config_file = self._get_server_template_dir(server_name) / f"{template_name}.json"

        custom_config_file = None
        if yaml_config_file.exists():
            custom_config_file = yaml_config_file
        elif json_config_file.exists():
            custom_config_file = json_config_file

        if custom_config_file:
            try:
                with open(custom_config_file, 'r', encoding='utf-8') as f:
                    custom_config = yaml.safe_load(f)

                # Deep merge custom config
                merged_config = self._deep_merge(merged_config, custom_config)
            except Exception as e:
                ch.warning(f"Failed to load custom config for '{template_name}': {e}")

        return merged_config

    def set_template_custom_value(
        self,
        server_name: str,
        template_name: str,
        key_path: str,
        value: Any
    ) -> bool:
        """
        Set a custom value for an template configuration.
        
        Creates custom config file if it doesn't exist.
        Marks template as customized in server YAML.
        
        Args:
            server_name: Name of the server
            template_name: Name of the template
            key_path: Dot-separated path to config key (e.g., 'paths.web_root')
            value: Value to set
        
        Returns:
            True if successful, False otherwise
        """
        # Load server config
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return False

        # Check if template is initialized
        templates = server_config.get('templates', {})
        if template_name not in templates:
            ch.error(f"Template '{template_name}' not initialized for server '{server_name}'")
            return False

        # Ensure template directory exists
        self._ensure_server_template_dir(server_name)

        # Load or create custom config (prefer YAML, migrate JSON if exists)
        yaml_config_file = self._get_server_template_dir(server_name) / f"{template_name}.yaml"
        json_config_file = self._get_server_template_dir(server_name) / f"{template_name}.json"

        custom_config = {}
        if yaml_config_file.exists():
            with open(yaml_config_file, 'r', encoding='utf-8') as f:
                custom_config = yaml.safe_load(f) or {}
        elif json_config_file.exists():
            # Migrate from JSON to YAML
            with open(json_config_file, 'r', encoding='utf-8') as f:
                import json
                custom_config = json.load(f)

        # Set value using key path
        keys = key_path.split('.')
        current = custom_config
        for key in keys[:-1]:
            if key not in current:
                current[key] = {}
            current = current[key]
        current[keys[-1]] = value

        # Save custom config as YAML
        with open(yaml_config_file, 'w', encoding='utf-8') as f:
            yaml.dump(custom_config, f, default_flow_style=False, sort_keys=False)

        # Mark as customized in server YAML
        server_config['templates'][template_name]['customized'] = True
        server_config['templates'][template_name]['last_modified'] = datetime.now().isoformat()
        self.config_manager.save_server_config(server_name, server_config)

        ch.success(f"Set {key_path} = {value} for template '{template_name}'")
        return True

    def enable_template(self, server_name: str, template_name: str) -> bool:
        """Enable an template for a specific server."""
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return False

        templates = server_config.get('templates', {})
        if template_name not in templates:
            ch.error(f"Template '{template_name}' not initialized. Initialize it first.")
            return False

        if templates[template_name].get('enabled'):
            ch.warning(f"Template '{template_name}' already enabled")
            return True

        templates[template_name]['enabled'] = True
        templates[template_name]['last_enabled'] = datetime.now().isoformat()
        self.config_manager.save_server_config(server_name, server_config)

        ch.success(f"Enabled template '{template_name}' for server '{server_name}'")
        return True

    def disable_template(self, server_name: str, template_name: str) -> bool:
        """Disable an template for a specific server."""
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return False

        templates = server_config.get('templates', {})
        if template_name not in templates:
            ch.warning(f"Template '{template_name}' not initialized for this server")
            return True

        if not templates[template_name].get('enabled'):
            ch.warning(f"Template '{template_name}' already disabled")
            return True

        templates[template_name]['enabled'] = False
        templates[template_name]['last_disabled'] = datetime.now().isoformat()
        self.config_manager.save_server_config(server_name, server_config)

        ch.success(f"Disabled template '{template_name}' for server '{server_name}'")
        return True

    def list_server_templates(self, server_name: str, enabled_only: bool = False) -> List[Dict[str, Any]]:
        """
        List all template configurations for a server.
        
        Args:
            server_name: Name of the server
            enabled_only: Only return enabled templates
        
        Returns:
            List of dicts with template info
        """
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            return []

        templates = server_config.get('templates', {})
        result = []

        for template_name, template_state in templates.items():
            if enabled_only and not template_state.get('enabled'):
                continue

            result.append({
                'name': template_name,
                'enabled': template_state.get('enabled', False),
                'template_version': template_state.get('template_version'),
                'auto_detected': template_state.get('auto_detected', False),
                'customized': template_state.get('customized', False),
                'last_synced': template_state.get('last_synced')
            })

        return sorted(result, key=lambda x: x['name'])

    def sync_template_from_template(
        self,
        server_name: str,
        template_name: str,
        preserve_custom: bool = True
    ) -> bool:
        """
        Sync template configuration from template.
        
        Updates template version and last_synced timestamp.
        Optionally preserves custom overrides.
        
        Args:
            server_name: Name of the server
            template_name: Name of the template
            preserve_custom: Keep custom overrides (default: True)
        
        Returns:
            True if successful, False otherwise
        """
        # Load server config
        try:
            server_config = self.config_manager.load_server_config(server_name)
        except FileNotFoundError:
            ch.error(f"Server '{server_name}' not found")
            return False

        # Check if template is initialized
        templates = server_config.get('templates', {})
        if template_name not in templates:
            ch.error(f"Template '{template_name}' not initialized for server '{server_name}'")
            return False

        # Get current template
        template = self.template_manager.get_template(template_name)
        if not template:
            ch.error(f"Template template '{template_name}' not found")
            return False

        # Update version and sync timestamp
        old_version = templates[template_name].get('template_version', 'unknown')
        new_version = template.metadata.get('version', '1.0.0')

        templates[template_name]['template_version'] = new_version
        templates[template_name]['last_synced'] = datetime.now().isoformat()

        # Save server config
        self.config_manager.save_server_config(server_name, server_config)

        if old_version != new_version:
            ch.success(f"Synced template '{template_name}' from template (v{old_version} -> v{new_version})")
        else:
            ch.success(f"Synced template '{template_name}' from template (v{new_version})")

        if preserve_custom:
            ch.dim("Custom overrides preserved")

        return True

    def _deep_merge(self, base: Dict, overlay: Dict) -> Dict:
        """
        Deep merge two dictionaries.
        
        Values from overlay override values in base.
        Recursively merges nested dicts.
        """
        result = copy.deepcopy(base)

        for key, value in overlay.items():
            if key in result and isinstance(result[key], dict) and isinstance(value, dict):
                result[key] = self._deep_merge(result[key], value)
            else:
                result[key] = copy.deepcopy(value)

        return result

