"""
Voice-to-OpenWork Pipeline
==========================
Bridges voice commands to OpenWork actions.

Features:
- Parse voice commands for OpenWork actions
- Route file operations, browser tasks, etc.
- Integration with OpenWork bridge
- Support for approval workflows

Usage:
    from core.voice.openwork_voice_bridge import VoiceToOpenWork

    bridge = VoiceToOpenWork()
    await bridge.process_voice_command("Open Chrome and go to GitHub")

Author: Genesis System
Version: 1.0.0
"""

import os
import sys
import re
import json
import asyncio
import logging
from datetime import datetime
from pathlib import Path
from typing import Optional, Dict, Any, List, Tuple
from dataclasses import dataclass, field
from enum import Enum, auto

# Add genesis path
GENESIS_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(GENESIS_ROOT))

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class OpenWorkActionType(Enum):
    """Types of actions for OpenWork."""
    BROWSER_OPEN = auto()
    BROWSER_NAVIGATE = auto()
    BROWSER_SEARCH = auto()
    FILE_CREATE = auto()
    FILE_READ = auto()
    FILE_WRITE = auto()
    FILE_DELETE = auto()
    FILE_RENAME = auto()
    FOLDER_CREATE = auto()
    FOLDER_LIST = auto()
    CLIPBOARD_COPY = auto()
    CLIPBOARD_PASTE = auto()
    APP_LAUNCH = auto()
    APP_CLOSE = auto()
    SYSTEM_COMMAND = auto()
    NOTIFICATION = auto()
    UNKNOWN = auto()


@dataclass
class ParsedVoiceAction:
    """A parsed voice command for OpenWork."""
    action_type: OpenWorkActionType
    raw_command: str
    parameters: Dict[str, Any] = field(default_factory=dict)
    confidence: float = 1.0
    requires_approval: bool = False

    def to_openwork_payload(self) -> Dict[str, Any]:
        """Convert to OpenWork action payload."""
        return {
            "action_type": self.action_type.name.lower(),
            "parameters": self.parameters,
            "source": "voice",
            "raw_command": self.raw_command,
            "timestamp": datetime.utcnow().isoformat()
        }


class VoiceCommandMatcher:
    """Pattern matching for voice commands to OpenWork actions."""

    # Browser patterns
    BROWSER_PATTERNS = [
        # Open browser
        (r"^open\s+(chrome|firefox|edge|browser|safari)$",
         OpenWorkActionType.BROWSER_OPEN,
         lambda m: {"browser": m.group(1)}),

        # Navigate to URL
        (r"^(go to|navigate to|open)\s+(https?://\S+)$",
         OpenWorkActionType.BROWSER_NAVIGATE,
         lambda m: {"url": m.group(2)}),

        # Navigate to site
        (r"^(go to|navigate to|open)\s+(\w+(?:\.\w+)+)$",
         OpenWorkActionType.BROWSER_NAVIGATE,
         lambda m: {"url": f"https://{m.group(2)}"}),

        # Search
        (r"^(search|google|look up)\s+(.+)$",
         OpenWorkActionType.BROWSER_SEARCH,
         lambda m: {"query": m.group(2)}),
    ]

    # File patterns
    FILE_PATTERNS = [
        # Create file
        (r"^create\s+(?:a\s+)?(?:new\s+)?file\s+(?:called\s+)?(.+)$",
         OpenWorkActionType.FILE_CREATE,
         lambda m: {"path": m.group(1).strip()}),

        # Read file
        (r"^(read|show|display|open)\s+(?:the\s+)?file\s+(.+)$",
         OpenWorkActionType.FILE_READ,
         lambda m: {"path": m.group(2).strip()}),

        # Write to file
        (r"^write\s+['\"](.+)['\"]\s+to\s+(?:file\s+)?(.+)$",
         OpenWorkActionType.FILE_WRITE,
         lambda m: {"content": m.group(1), "path": m.group(2).strip()}),

        # Delete file
        (r"^delete\s+(?:the\s+)?file\s+(.+)$",
         OpenWorkActionType.FILE_DELETE,
         lambda m: {"path": m.group(1).strip()}),

        # Rename file
        (r"^rename\s+(?:file\s+)?(.+)\s+to\s+(.+)$",
         OpenWorkActionType.FILE_RENAME,
         lambda m: {"old_path": m.group(1).strip(), "new_path": m.group(2).strip()}),
    ]

    # Folder patterns
    FOLDER_PATTERNS = [
        # Create folder
        (r"^create\s+(?:a\s+)?(?:new\s+)?folder\s+(?:called\s+)?(.+)$",
         OpenWorkActionType.FOLDER_CREATE,
         lambda m: {"path": m.group(1).strip()}),

        # List folder
        (r"^(list|show)\s+(?:files\s+in\s+)?(?:the\s+)?folder\s+(.+)$",
         OpenWorkActionType.FOLDER_LIST,
         lambda m: {"path": m.group(2).strip()}),
    ]

    # Clipboard patterns
    CLIPBOARD_PATTERNS = [
        # Copy to clipboard
        (r"^copy\s+['\"](.+)['\"](?:\s+to\s+clipboard)?$",
         OpenWorkActionType.CLIPBOARD_COPY,
         lambda m: {"content": m.group(1)}),

        # Paste from clipboard
        (r"^paste(?:\s+from\s+clipboard)?$",
         OpenWorkActionType.CLIPBOARD_PASTE,
         lambda m: {}),
    ]

    # App patterns
    APP_PATTERNS = [
        # Launch app
        (r"^(launch|start|open)\s+(?:the\s+)?(?:app\s+)?(\w+)$",
         OpenWorkActionType.APP_LAUNCH,
         lambda m: {"app_name": m.group(2)}),

        # Close app
        (r"^(close|quit|exit)\s+(?:the\s+)?(?:app\s+)?(\w+)$",
         OpenWorkActionType.APP_CLOSE,
         lambda m: {"app_name": m.group(2)}),
    ]

    # System patterns
    SYSTEM_PATTERNS = [
        # Run command
        (r"^run\s+(?:command\s+)?(.+)$",
         OpenWorkActionType.SYSTEM_COMMAND,
         lambda m: {"command": m.group(1)}),

        # Notification
        (r"^notify\s+['\"](.+)['\"]$",
         OpenWorkActionType.NOTIFICATION,
         lambda m: {"message": m.group(1)}),
    ]

    # High-risk actions requiring approval
    HIGH_RISK_ACTIONS = {
        OpenWorkActionType.FILE_DELETE,
        OpenWorkActionType.SYSTEM_COMMAND,
    }

    @classmethod
    def all_patterns(cls) -> List[Tuple]:
        """Get all patterns."""
        return (
            cls.BROWSER_PATTERNS +
            cls.FILE_PATTERNS +
            cls.FOLDER_PATTERNS +
            cls.CLIPBOARD_PATTERNS +
            cls.APP_PATTERNS +
            cls.SYSTEM_PATTERNS
        )

    @classmethod
    def match(cls, text: str) -> Optional[ParsedVoiceAction]:
        """Match voice command to action."""
        text_lower = text.lower().strip()

        for pattern, action_type, param_extractor in cls.all_patterns():
            match = re.match(pattern, text_lower, re.IGNORECASE)
            if match:
                params = param_extractor(match)
                requires_approval = action_type in cls.HIGH_RISK_ACTIONS

                return ParsedVoiceAction(
                    action_type=action_type,
                    raw_command=text,
                    parameters=params,
                    confidence=1.0,
                    requires_approval=requires_approval
                )

        return None


class VoiceToOpenWork:
    """
    Main voice-to-OpenWork pipeline.

    Connects voice input to OpenWork actions through the bridge.
    """

    def __init__(
        self,
        openwork_bridge=None,
        auto_execute: bool = True,
        require_approval_for_high_risk: bool = True
    ):
        self._bridge = openwork_bridge
        self.auto_execute = auto_execute
        self.require_approval = require_approval_for_high_risk

        # Action history
        self.action_history: List[ParsedVoiceAction] = []
        self.max_history = 100

        # Callbacks
        self._pre_execute_callback = None
        self._post_execute_callback = None

        logger.info("VoiceToOpenWork pipeline initialized")

    def set_bridge(self, bridge):
        """Set the OpenWork bridge."""
        self._bridge = bridge

    async def process_voice_command(
        self,
        command: str
    ) -> Optional[ParsedVoiceAction]:
        """
        Process a voice command and optionally execute it.

        Args:
            command: Voice command text

        Returns:
            ParsedVoiceAction if successfully parsed
        """
        # Parse command
        action = VoiceCommandMatcher.match(command)

        if not action:
            logger.info(f"No OpenWork action matched for: {command}")
            return None

        # Add to history
        self.action_history.append(action)
        if len(self.action_history) > self.max_history:
            self.action_history.pop(0)

        logger.info(f"Parsed voice action: {action.action_type.name} - {action.parameters}")

        # Pre-execute callback
        if self._pre_execute_callback:
            should_continue = await self._pre_execute_callback(action)
            if not should_continue:
                logger.info("Action cancelled by pre-execute callback")
                return action

        # Execute if auto_execute enabled
        if self.auto_execute and self._bridge:
            await self._execute_action(action)

        # Post-execute callback
        if self._post_execute_callback:
            await self._post_execute_callback(action)

        return action

    async def _execute_action(self, action: ParsedVoiceAction):
        """Execute action through OpenWork bridge."""
        if not self._bridge:
            logger.warning("No OpenWork bridge configured")
            return

        # Check if approval required
        requires_approval = (
            self.require_approval and action.requires_approval
        )

        # Map action type to bridge method
        action_type_map = {
            OpenWorkActionType.BROWSER_OPEN: "browser_task",
            OpenWorkActionType.BROWSER_NAVIGATE: "browser_task",
            OpenWorkActionType.BROWSER_SEARCH: "browser_task",
            OpenWorkActionType.FILE_CREATE: "file_operation",
            OpenWorkActionType.FILE_READ: "file_operation",
            OpenWorkActionType.FILE_WRITE: "file_operation",
            OpenWorkActionType.FILE_DELETE: "file_operation",
            OpenWorkActionType.FILE_RENAME: "file_operation",
            OpenWorkActionType.FOLDER_CREATE: "file_operation",
            OpenWorkActionType.FOLDER_LIST: "file_operation",
            OpenWorkActionType.CLIPBOARD_COPY: "clipboard",
            OpenWorkActionType.CLIPBOARD_PASTE: "clipboard",
            OpenWorkActionType.APP_LAUNCH: "system_command",
            OpenWorkActionType.APP_CLOSE: "system_command",
            OpenWorkActionType.SYSTEM_COMMAND: "system_command",
            OpenWorkActionType.NOTIFICATION: "notification",
        }

        bridge_action_type = action_type_map.get(
            action.action_type,
            "system_command"
        )

        payload = action.to_openwork_payload()

        try:
            await self._bridge.send_action(
                action_type=bridge_action_type,
                payload=payload,
                requires_approval=requires_approval
            )
            logger.info(f"Action sent to OpenWork: {action.action_type.name}")

        except Exception as e:
            logger.error(f"Failed to send action to OpenWork: {e}")

    def on_pre_execute(self, callback):
        """Register pre-execute callback. Return False to cancel."""
        self._pre_execute_callback = callback

    def on_post_execute(self, callback):
        """Register post-execute callback."""
        self._post_execute_callback = callback

    def get_history(self, limit: int = 10) -> List[Dict[str, Any]]:
        """Get action history."""
        return [
            {
                "action_type": a.action_type.name,
                "raw_command": a.raw_command,
                "parameters": a.parameters,
                "requires_approval": a.requires_approval
            }
            for a in self.action_history[-limit:]
        ]

    @staticmethod
    def get_supported_commands() -> Dict[str, List[str]]:
        """Get list of supported voice commands."""
        return {
            "browser": [
                "open chrome/firefox/edge/browser",
                "go to [url]",
                "navigate to [website]",
                "search [query]"
            ],
            "files": [
                "create file [path]",
                "read file [path]",
                "write '[content]' to [path]",
                "delete file [path]",
                "rename [old] to [new]"
            ],
            "folders": [
                "create folder [path]",
                "list folder [path]"
            ],
            "clipboard": [
                "copy '[text]'",
                "paste"
            ],
            "apps": [
                "launch [app]",
                "close [app]"
            ],
            "system": [
                "run [command]",
                "notify '[message]'"
            ]
        }


# Integration with voice channel
async def create_voice_openwork_pipeline():
    """Create integrated voice-to-OpenWork pipeline."""
    try:
        from core.integrations.openwork_bridge import OpenWorkBridge
        from core.voice.kinan_aiva_voice_channel import KinanAivaVoiceChannel

        # Create bridge
        bridge = OpenWorkBridge(mode="server")

        # Create voice pipeline
        pipeline = VoiceToOpenWork(openwork_bridge=bridge)

        # Create voice channel
        voice_channel = KinanAivaVoiceChannel()

        # Connect voice channel to pipeline
        @voice_channel.on_message
        async def handle_voice(message):
            if message.direction == "kinan_to_aiva":
                action = await pipeline.process_voice_command(message.text)
                if action:
                    # Speak confirmation
                    await voice_channel.speak_to_kinan(
                        f"Executing {action.action_type.name.replace('_', ' ').lower()}"
                    )

        return {
            "bridge": bridge,
            "pipeline": pipeline,
            "voice_channel": voice_channel
        }

    except ImportError as e:
        logger.error(f"Failed to create pipeline: {e}")
        return None


# CLI for testing
async def main():
    """CLI for testing voice-to-OpenWork pipeline."""
    import argparse

    parser = argparse.ArgumentParser(description="Voice-to-OpenWork Pipeline")
    parser.add_argument("--command", "-c", type=str, help="Voice command to parse")
    parser.add_argument("--list", action="store_true", help="List supported commands")
    parser.add_argument("--test", action="store_true", help="Run tests")
    args = parser.parse_args()

    if args.list:
        commands = VoiceToOpenWork.get_supported_commands()
        print("\nSupported Voice Commands for OpenWork:")
        print("=" * 50)
        for category, examples in commands.items():
            print(f"\n{category.upper()}:")
            for example in examples:
                print(f"  - {example}")
        return

    if args.test:
        print("Testing voice command parsing...\n")

        test_commands = [
            "open chrome",
            "go to github.com",
            "search python tutorials",
            "create file test.txt",
            "read file README.md",
            "delete file temp.txt",
            "create folder new_project",
            "copy 'hello world'",
            "launch vscode",
            "run ls -la",
            "this is not a command"
        ]

        pipeline = VoiceToOpenWork(auto_execute=False)

        for cmd in test_commands:
            action = await pipeline.process_voice_command(cmd)
            if action:
                print(f"  '{cmd}'")
                print(f"    -> {action.action_type.name}")
                print(f"    -> params: {action.parameters}")
                print(f"    -> approval: {action.requires_approval}")
            else:
                print(f"  '{cmd}' -> NO MATCH")
            print()

        return

    if args.command:
        pipeline = VoiceToOpenWork(auto_execute=False)
        action = await pipeline.process_voice_command(args.command)

        if action:
            print(f"\nParsed Action:")
            print(f"  Type: {action.action_type.name}")
            print(f"  Parameters: {json.dumps(action.parameters, indent=4)}")
            print(f"  Requires Approval: {action.requires_approval}")
            print(f"\nOpenWork Payload:")
            print(json.dumps(action.to_openwork_payload(), indent=2))
        else:
            print(f"\nNo OpenWork action matched for: {args.command}")
            print("\nTry: python openwork_voice_bridge.py --list")

    else:
        parser.print_help()


if __name__ == "__main__":
    asyncio.run(main())
