"""
Resources API for CARL Control Panel.
Handles discovery, installation, and management of modular resources (hooks, skills, commands).
"""
import json
import shutil
from pathlib import Path
from datetime import datetime
from typing import Optional


# Bundled resources location
BUNDLED_RESOURCES_DIR = Path(__file__).parent.parent / 'static' / 'resources'


class ResourceManager:
    """Manages resource discovery, installation, and state."""

    def __init__(self, carl_path: Path, claude_path: Optional[Path] = None):
        """Initialize with paths.

        Args:
            carl_path: Path to .carl directory (workspace tracking)
            claude_path: Path to ~/.claude directory (install targets)
        """
        self.carl_path = Path(carl_path)
        self.claude_path = claude_path or Path.home() / '.claude'
        self.registry_path = self.carl_path / 'resources.json'
        self.manifest_path = self.carl_path / 'manifest'

    def _load_registry(self) -> dict:
        """Load the resources registry from .carl/resources.json."""
        if self.registry_path.exists():
            try:
                return json.loads(self.registry_path.read_text())
            except Exception:
                pass
        return {'installed': {}}

    def _save_registry(self, registry: dict):
        """Save the resources registry."""
        self.carl_path.mkdir(parents=True, exist_ok=True)
        self.registry_path.write_text(json.dumps(registry, indent=2))

    def _get_manifest_value(self, key: str) -> Optional[str]:
        """Get a value from the manifest file."""
        if not self.manifest_path.exists():
            return None
        try:
            content = self.manifest_path.read_text()
            for line in content.splitlines():
                if line.startswith(f'{key}='):
                    return line.split('=', 1)[1].strip()
        except Exception:
            pass
        return None

    def _set_manifest_value(self, key: str, value: str):
        """Set a value in the manifest file."""
        self.carl_path.mkdir(parents=True, exist_ok=True)

        lines = []
        found = False

        if self.manifest_path.exists():
            content = self.manifest_path.read_text()
            for line in content.splitlines():
                if line.startswith(f'{key}='):
                    lines.append(f'{key}={value}')
                    found = True
                else:
                    lines.append(line)

        if not found:
            lines.append(f'{key}={value}')

        self.manifest_path.write_text('\n'.join(lines))

    def _remove_manifest_key(self, key: str):
        """Remove a key from the manifest file."""
        if not self.manifest_path.exists():
            return

        content = self.manifest_path.read_text()
        lines = [line for line in content.splitlines() if not line.startswith(f'{key}=')]
        self.manifest_path.write_text('\n'.join(lines))

    def discover_resources(self) -> dict:
        """Discover all available resources from bundled directory.

        Returns:
            Dict with 'success' and 'resources' list.
        """
        resources = []
        registry = self._load_registry()
        installed = registry.get('installed', {})

        if not BUNDLED_RESOURCES_DIR.exists():
            return {'success': True, 'resources': []}

        for resource_dir in BUNDLED_RESOURCES_DIR.iterdir():
            if not resource_dir.is_dir():
                continue

            config_path = resource_dir / 'config.json'
            if not config_path.exists():
                continue

            try:
                config = json.loads(config_path.read_text())
            except Exception:
                continue

            # Skip resources that are explicitly disabled (enabled: false)
            if config.get('enabled', True) is False:
                continue

            resource_id = config.get('id', resource_dir.name)

            # Determine status
            status = 'not_installed'
            global_enabled = True
            wired = False

            if resource_id in installed:
                install_info = installed[resource_id]
                wired = install_info.get('wired', False)
                status = 'wired' if wired else 'installed'

                # Check manifest for global enabled state
                manifest_key = None
                if 'hooks' in config:
                    first_hook = list(config['hooks'].values())[0]
                    manifest_key = first_hook.get('manifest_key')

                if manifest_key:
                    state = self._get_manifest_value(f'{manifest_key}_GLOBAL')
                    global_enabled = state != 'false'

            resources.append({
                'id': resource_id,
                'name': config.get('name', resource_id),
                'description': config.get('description', ''),
                'version': config.get('version', '1.0.0'),
                'author': config.get('author', ''),
                'types': config.get('types', []),
                'status': status,
                'global_enabled': global_enabled,
                'source': 'bundled',
                'path': str(resource_dir),
                'hooks': config.get('hooks', {}),
                'skills': config.get('skills', {}),
                'commands': config.get('commands', {})
            })

        return {'success': True, 'resources': resources}

    def install_resource(self, resource_id: str) -> dict:
        """Install a resource (copy files to targets).

        Args:
            resource_id: The resource ID to install.

        Returns:
            Dict with success status.
        """
        # Find the resource
        resource_dir = BUNDLED_RESOURCES_DIR / resource_id
        config_path = resource_dir / 'config.json'

        if not config_path.exists():
            return {'success': False, 'error': f'Resource not found: {resource_id}'}

        try:
            config = json.loads(config_path.read_text())
        except Exception as e:
            return {'success': False, 'error': f'Invalid config: {e}'}

        installed_files = {'hooks': [], 'skills': [], 'commands': []}

        # Install hooks
        hooks_dir = resource_dir / 'hooks'
        if hooks_dir.exists():
            target_dir = self.claude_path / 'hooks'
            target_dir.mkdir(parents=True, exist_ok=True)

            for hook_file in hooks_dir.glob('*.py'):
                target = target_dir / hook_file.name
                shutil.copy2(hook_file, target)
                installed_files['hooks'].append(hook_file.name)

        # Install skills (skills are folders with SKILL.md entry point)
        skills_dir = resource_dir / 'skills'
        if skills_dir.exists():
            target_dir = self.claude_path / 'skills'
            target_dir.mkdir(parents=True, exist_ok=True)

            for skill_folder in skills_dir.iterdir():
                if skill_folder.is_dir() and (skill_folder / 'SKILL.md').exists():
                    # Copy entire skill folder
                    target = target_dir / skill_folder.name
                    if target.exists():
                        shutil.rmtree(target)
                    shutil.copytree(skill_folder, target)
                    installed_files['skills'].append(skill_folder.name)

        # Install commands
        commands_dir = resource_dir / 'commands'
        if commands_dir.exists():
            target_dir = self.claude_path / 'commands'
            target_dir.mkdir(parents=True, exist_ok=True)

            for cmd_item in commands_dir.glob('*'):
                target = target_dir / cmd_item.name
                if cmd_item.is_dir():
                    # Copy entire command folder (with tasks/, templates/, etc.)
                    if target.exists():
                        shutil.rmtree(target)
                    shutil.copytree(cmd_item, target)
                else:
                    # Copy single command file
                    shutil.copy2(cmd_item, target)
                installed_files['commands'].append(cmd_item.name)

        # Update registry
        registry = self._load_registry()
        registry['installed'][resource_id] = {
            'version': config.get('version', '1.0.0'),
            'installed_at': datetime.now().isoformat(),
            'types': config.get('types', []),
            'files': installed_files,
            'wired': False,
            'install_path': str(self.claude_path)  # Track where installed
        }
        self._save_registry(registry)

        # Determine if wiring is needed
        needs_wiring = len(installed_files['hooks']) > 0

        return {
            'success': True,
            'status': 'installed',
            'needs_wiring': needs_wiring,
            'files': installed_files
        }

    def wire_resource(self, resource_id: str) -> dict:
        """Wire a resource's hooks to settings.json.

        Args:
            resource_id: The resource ID to wire.

        Returns:
            Dict with success status.
        """
        registry = self._load_registry()
        if resource_id not in registry.get('installed', {}):
            return {'success': False, 'error': 'Resource not installed'}

        install_info = registry['installed'][resource_id]
        # Use recorded install path for hook file location
        install_path = Path(install_info.get('install_path', str(self.claude_path)))

        # Find the resource config
        resource_dir = BUNDLED_RESOURCES_DIR / resource_id
        config_path = resource_dir / 'config.json'

        if not config_path.exists():
            return {'success': False, 'error': 'Resource config not found'}

        try:
            config = json.loads(config_path.read_text())
        except Exception as e:
            return {'success': False, 'error': f'Invalid config: {e}'}

        hooks_config = config.get('hooks', {})
        if not hooks_config:
            return {'success': False, 'error': 'No hooks to wire'}

        # Read settings.json (always use global ~/.claude/settings.json for wiring)
        settings_path = Path.home() / '.claude' / 'settings.json'
        settings_path.parent.mkdir(parents=True, exist_ok=True)
        settings = {}
        if settings_path.exists():
            try:
                settings = json.loads(settings_path.read_text())
            except Exception:
                settings = {}

        hooks_array = settings.get('hooks', [])

        # Add hook entries (using the actual install location)
        for hook_file, hook_config in hooks_config.items():
            hook_path = str(install_path / 'hooks' / hook_file)
            hook_type = hook_config.get('hook_type', 'UserPromptSubmit')

            # Check if already wired
            already_wired = any(
                h.get('command') == f'python3 {hook_path}' or
                h.get('command') == f'python {hook_path}'
                for h in hooks_array
            )

            if not already_wired:
                hook_entry = {
                    'type': hook_type,
                    'command': f'python3 {hook_path}'
                }
                hooks_array.append(hook_entry)

        # Save settings.json
        settings['hooks'] = hooks_array
        settings_path.write_text(json.dumps(settings, indent=2))

        # Update manifest with toggle
        for hook_file, hook_config in hooks_config.items():
            manifest_key = hook_config.get('manifest_key')
            if manifest_key:
                self._set_manifest_value(f'{manifest_key}_STATE', 'active')
                self._set_manifest_value(f'{manifest_key}_GLOBAL', 'true')

        # Update registry
        registry['installed'][resource_id]['wired'] = True
        self._save_registry(registry)

        return {'success': True, 'status': 'wired'}

    def toggle_resource(self, resource_id: str, enabled: bool) -> dict:
        """Toggle a resource's global enabled state.

        Args:
            resource_id: The resource ID.
            enabled: Whether to enable or disable.

        Returns:
            Dict with success status.
        """
        # Find the resource config
        resource_dir = BUNDLED_RESOURCES_DIR / resource_id
        config_path = resource_dir / 'config.json'

        if not config_path.exists():
            return {'success': False, 'error': 'Resource not found'}

        try:
            config = json.loads(config_path.read_text())
        except Exception as e:
            return {'success': False, 'error': f'Invalid config: {e}'}

        # Update manifest for each hook
        for hook_file, hook_config in config.get('hooks', {}).items():
            manifest_key = hook_config.get('manifest_key')
            if manifest_key:
                self._set_manifest_value(f'{manifest_key}_GLOBAL', 'true' if enabled else 'false')

        return {'success': True, 'enabled': enabled}

    def uninstall_resource(self, resource_id: str, delete_files: bool = False) -> dict:
        """Uninstall a resource (remove from settings.json, optionally delete files).

        Args:
            resource_id: The resource ID.
            delete_files: Whether to delete the installed files.

        Returns:
            Dict with success status.
        """
        registry = self._load_registry()
        if resource_id not in registry.get('installed', {}):
            return {'success': False, 'error': 'Resource not installed'}

        install_info = registry['installed'][resource_id]

        # Use recorded install path, fallback to self.claude_path
        install_path = Path(install_info.get('install_path', str(self.claude_path)))

        # Find the resource config
        resource_dir = BUNDLED_RESOURCES_DIR / resource_id
        config_path = resource_dir / 'config.json'
        config = {}
        if config_path.exists():
            try:
                config = json.loads(config_path.read_text())
            except Exception:
                pass

        # Remove from settings.json (always check global ~/.claude/settings.json)
        settings_path = Path.home() / '.claude' / 'settings.json'
        if settings_path.exists():
            try:
                settings = json.loads(settings_path.read_text())
                hooks_array = settings.get('hooks', [])

                # Remove hook entries for this resource (check both install locations)
                for hook_file in install_info.get('files', {}).get('hooks', []):
                    hook_path = str(install_path / 'hooks' / hook_file)
                    hooks_array = [
                        h for h in hooks_array
                        if h.get('command') not in [f'python3 {hook_path}', f'python {hook_path}']
                    ]

                settings['hooks'] = hooks_array
                settings_path.write_text(json.dumps(settings, indent=2))
            except Exception:
                pass

        # Remove manifest entries
        for hook_file, hook_config in config.get('hooks', {}).items():
            manifest_key = hook_config.get('manifest_key')
            if manifest_key:
                self._remove_manifest_key(f'{manifest_key}_STATE')
                self._remove_manifest_key(f'{manifest_key}_GLOBAL')

        # Optionally delete files (from recorded install location)
        if delete_files:
            for hook_file in install_info.get('files', {}).get('hooks', []):
                target = install_path / 'hooks' / hook_file
                if target.exists():
                    target.unlink()

            for skill_name in install_info.get('files', {}).get('skills', []):
                target = install_path / 'skills' / skill_name
                if target.exists():
                    if target.is_dir():
                        shutil.rmtree(target)
                    else:
                        target.unlink()

            for cmd_file in install_info.get('files', {}).get('commands', []):
                target = install_path / 'commands' / cmd_file
                if target.exists():
                    target.unlink()

        # Update registry
        del registry['installed'][resource_id]
        self._save_registry(registry)

        return {'success': True}

    def get_installed_hook_resources(self) -> list:
        """Get list of installed resources that have hooks (for Settings tab toggles).

        Returns:
            List of resource info dicts with toggle state.
        """
        resources = []
        registry = self._load_registry()

        for resource_id, install_info in registry.get('installed', {}).items():
            if 'hook' not in install_info.get('types', []):
                continue

            if not install_info.get('wired', False):
                continue

            # Find config
            resource_dir = BUNDLED_RESOURCES_DIR / resource_id
            config_path = resource_dir / 'config.json'
            if not config_path.exists():
                continue

            try:
                config = json.loads(config_path.read_text())
            except Exception:
                continue

            # Get global enabled state from manifest
            global_enabled = True
            for hook_file, hook_config in config.get('hooks', {}).items():
                manifest_key = hook_config.get('manifest_key')
                if manifest_key:
                    state = self._get_manifest_value(f'{manifest_key}_GLOBAL')
                    global_enabled = state != 'false'
                    break

            resources.append({
                'id': resource_id,
                'name': config.get('name', resource_id),
                'description': config.get('description', ''),
                'manifest_key': config.get('hooks', {}).get(list(config.get('hooks', {}).keys())[0], {}).get('manifest_key') if config.get('hooks') else None,
                'global_enabled': global_enabled
            })

        return resources

    def get_snippet(self, resource_id: str) -> dict:
        """Get the settings.json snippet for a resource's hooks.

        Args:
            resource_id: The resource ID.

        Returns:
            Dict with success and snippet string.
        """
        # Check registry for install path
        registry = self._load_registry()
        install_path = self.claude_path
        if resource_id in registry.get('installed', {}):
            install_info = registry['installed'][resource_id]
            install_path = Path(install_info.get('install_path', str(self.claude_path)))

        resource_dir = BUNDLED_RESOURCES_DIR / resource_id
        config_path = resource_dir / 'config.json'

        if not config_path.exists():
            return {'success': False, 'error': 'Resource not found'}

        try:
            config = json.loads(config_path.read_text())
        except Exception as e:
            return {'success': False, 'error': f'Invalid config: {e}'}

        hooks_config = config.get('hooks', {})
        if not hooks_config:
            return {'success': False, 'error': 'No hooks in resource'}

        snippets = []
        for hook_file, hook_config in hooks_config.items():
            hook_path = str(install_path / 'hooks' / hook_file)
            hook_type = hook_config.get('hook_type', 'UserPromptSubmit')

            snippet = {
                'type': hook_type,
                'command': f'python3 {hook_path}'
            }
            snippets.append(json.dumps(snippet, indent=2))

        return {
            'success': True,
            'snippet': ',\n'.join(snippets)
        }


# Module-level convenience functions for API routes

def discover_resources(carl_path: str, claude_path: str = None) -> dict:
    """Discover all available resources."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.discover_resources()


def install_resource(carl_path: str, resource_id: str, claude_path: str = None) -> dict:
    """Install a resource."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.install_resource(resource_id)


def wire_resource(carl_path: str, resource_id: str, claude_path: str = None) -> dict:
    """Wire a resource's hooks."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.wire_resource(resource_id)


def toggle_resource(carl_path: str, resource_id: str, enabled: bool, claude_path: str = None) -> dict:
    """Toggle a resource's global state."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.toggle_resource(resource_id, enabled)


def uninstall_resource(carl_path: str, resource_id: str, delete_files: bool = False, claude_path: str = None) -> dict:
    """Uninstall a resource."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.uninstall_resource(resource_id, delete_files)


def get_installed_hook_resources(carl_path: str) -> list:
    """Get installed hook resources for Settings toggles."""
    manager = ResourceManager(Path(carl_path))
    return manager.get_installed_hook_resources()


def get_resource_snippet(carl_path: str, resource_id: str, claude_path: str = None) -> dict:
    """Get settings.json snippet for a resource."""
    manager = ResourceManager(Path(carl_path), Path(claude_path) if claude_path else None)
    return manager.get_snippet(resource_id)
