"""
Professional Automation Framework for Windows
Record, edit, and playback mouse/keyboard automation scripts
"""

import json
import time
from datetime import datetime
from typing import List, Dict, Optional, Callable
from pathlib import Path

# Import our existing modules
from . import clipboard, screen, notifications
from .inputs import (
    click, move_mouse, press_key, type_text, send_keys,
    get_click, get_key, on_click_event, on_key_event
)


class AutomationAction:
    """Base class for automation actions"""

    def __init__(self, action_type: str, **kwargs):
        self.type = action_type
        self.enabled = kwargs.get('enabled', True)
        self.id = kwargs.get('id', str(int(time.time() * 1000)))
        self.properties = kwargs

    def execute(self, context: Dict):
        """Execute this action"""
        pass

    def to_dict(self) -> Dict:
        """Serialize to dictionary"""
        return {
            'type': self.type,
            'id': self.id,
            'enabled': self.enabled,
            **self.properties
        }

    def to_python(self) -> str:
        """Convert to Python code"""
        return f"# {self.type} action"


class ClickAction(AutomationAction):
    """Mouse click action"""

    def __init__(self, x: int, y: int, button: str = 'left', clicks: int = 1, **kwargs):
        super().__init__('click', x=x, y=y, button=button, clicks=clicks, **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        click(self.properties['x'], self.properties['y'],
              self.properties['button'], self.properties.get('clicks', 1))

    def to_python(self) -> str:
        return f"click({self.properties['x']}, {self.properties['y']}, button='{self.properties['button']}')"

    def __str__(self):
        return f"Click {self.properties['button']} at ({self.properties['x']}, {self.properties['y']})"


class TypeAction(AutomationAction):
    """Type text action"""

    def __init__(self, text: str, **kwargs):
        super().__init__('type', text=text, **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        # Replace variables
        text = self.properties['text']
        for var_name, var_value in context.get('variables', {}).items():
            text = text.replace(f"{{{var_name}}}", str(var_value))
        type_text(text)

    def to_python(self) -> str:
        text = self.properties['text'].replace('"', '\\"')
        return f'type_text("{text}")'

    def __str__(self):
        text = self.properties['text']
        preview = text[:30] + "..." if len(text) > 30 else text
        return f'Type "{preview}"'


class KeyAction(AutomationAction):
    """Press key action"""

    def __init__(self, key: str, modifiers: List[str] = None, **kwargs):
        super().__init__('key', key=key, modifiers=modifiers or [], **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        modifiers = self.properties.get('modifiers', [])
        key = self.properties['key']

        if modifiers:
            combo = '+'.join(modifiers + [key])
            send_keys(combo)
        else:
            press_key(key)

    def to_python(self) -> str:
        modifiers = self.properties.get('modifiers', [])
        key = self.properties['key']
        if modifiers:
            combo = '+'.join(modifiers + [key])
            return f"send_keys('{combo}')"
        return f"press_key('{key}')"

    def __str__(self):
        modifiers = self.properties.get('modifiers', [])
        key = self.properties['key']
        if modifiers:
            return f"Press {'+'.join(modifiers)}+{key}"
        return f"Press {key}"


class DelayAction(AutomationAction):
    """Delay/wait action"""

    def __init__(self, milliseconds: int, **kwargs):
        super().__init__('delay', milliseconds=milliseconds, **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        time.sleep(self.properties['milliseconds'] / 1000.0)

    def to_python(self) -> str:
        seconds = self.properties['milliseconds'] / 1000.0
        return f"time.sleep({seconds})"

    def __str__(self):
        return f"Wait {self.properties['milliseconds']}ms"


class ScreenshotAction(AutomationAction):
    """Screenshot action"""

    def __init__(self, filepath: str, **kwargs):
        super().__init__('screenshot', filepath=filepath, **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        screen.screenshot(self.properties['filepath'])

    def to_python(self) -> str:
        return f"screen.screenshot('{self.properties['filepath']}')"

    def __str__(self):
        return f"Screenshot to {Path(self.properties['filepath']).name}"


class SetVariableAction(AutomationAction):
    """Set variable action"""

    def __init__(self, name: str, value: str, **kwargs):
        super().__init__('setvar', name=name, value=value, **kwargs)

    def execute(self, context: Dict):
        if not self.enabled:
            return
        if 'variables' not in context:
            context['variables'] = {}
        context['variables'][self.properties['name']] = self.properties['value']

    def to_python(self) -> str:
        return f"{self.properties['name']} = '{self.properties['value']}'"

    def __str__(self):
        return f"Set {self.properties['name']} = {self.properties['value']}"


class AutomationScript:
    """Automation script with actions"""

    def __init__(self, name: str = "New Script"):
        self.name = name
        self.description = ""
        self.actions: List[AutomationAction] = []
        self.metadata = {
            'created': datetime.now().isoformat(),
            'version': '1.0'
        }

    def add_action(self, action: AutomationAction):
        """Add an action to the script"""
        self.actions.append(action)

    def remove_action(self, index: int):
        """Remove action at index"""
        if 0 <= index < len(self.actions):
            del self.actions[index]

    def clear(self):
        """Clear all actions"""
        self.actions.clear()

    def save(self, filepath: str):
        """Save script to JSON file"""
        data = {
            'name': self.name,
            'description': self.description,
            'metadata': self.metadata,
            'actions': [action.to_dict() for action in self.actions]
        }

        with open(filepath, 'w') as f:
            json.dump(data, f, indent=2)

    @staticmethod
    def load(filepath: str) -> 'AutomationScript':
        """Load script from JSON file"""
        with open(filepath, 'r') as f:
            data = json.load(f)

        script = AutomationScript(data.get('name', 'Loaded Script'))
        script.description = data.get('description', '')
        script.metadata = data.get('metadata', {})

        for action_data in data.get('actions', []):
            action = _create_action_from_dict(action_data)
            if action:
                script.actions.append(action)

        return script

    def export_to_python(self, filepath: Optional[str] = None) -> str:
        """Export script as Python code"""
        lines = [
            "# Generated by Alyios Automation Framework",
            f"# Script: {self.name}",
            f"# Created: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
            "",
            "from AlyiosWindowsFunctions import click, type_text, press_key, send_keys, screen",
            "import time",
            "",
            f"def {self.name.lower().replace(' ', '_')}():",
            f'    """{self.description}"""',
        ]

        for action in self.actions:
            lines.append(f"    {action.to_python()}")

        lines.extend([
            "",
            "if __name__ == '__main__':",
            f"    {self.name.lower().replace(' ', '_')}()"
        ])

        code = '\n'.join(lines)

        if filepath:
            with open(filepath, 'w') as f:
                f.write(code)

        return code

    def play(self, loop_count: int = 1, speed: float = 1.0,
             on_progress: Optional[Callable] = None):
        """Play the script"""
        context = {'variables': {}}

        for loop in range(loop_count):
            for i, action in enumerate(self.actions):
                # Apply speed multiplier to delays
                if isinstance(action, DelayAction) and abs(speed - 1.0) > 0.001:
                    original_ms = action.properties['milliseconds']
                    action.properties['milliseconds'] = int(original_ms / speed)

                action.execute(context)

                # Restore original delay
                if isinstance(action, DelayAction) and abs(speed - 1.0) > 0.001:
                    action.properties['milliseconds'] = original_ms

                if on_progress:
                    on_progress(loop + 1, i + 1, len(self.actions))

    def __len__(self):
        return len(self.actions)

    def __getitem__(self, index):
        return self.actions[index]


class Recorder:
    """Record user actions into an automation script"""

    def __init__(self):
        self.script = AutomationScript()
        self.is_recording = False
        self.last_action_time = None
        self.mouse_listener = None
        self.min_delay_ms = 100

    def start(self, script_name: str = "Recorded Script"):
        """Start recording"""
        self.script = AutomationScript(script_name)
        self.script.description = f"Recorded on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        self.is_recording = True
        self.last_action_time = datetime.now()

        # Start mouse hook
        def handle_click(x, y, button, pressed):
            if pressed and self.is_recording:
                self._record_click(x, y, button.lower())

        self.mouse_listener = on_click_event(handle_click)

    def stop(self) -> AutomationScript:
        """Stop recording and return the script"""
        self.is_recording = False
        if self.mouse_listener:
            self.mouse_listener.stop()
        return self.script

    def _record_click(self, x: int, y: int, button: str):
        """Record a click action"""
        self._add_delay_if_needed()
        action = ClickAction(x, y, button)
        self.script.add_action(action)
        self.last_action_time = datetime.now()

    def record_type(self, text: str):
        """Manually record typing"""
        self._add_delay_if_needed()
        action = TypeAction(text)
        self.script.add_action(action)
        self.last_action_time = datetime.now()

    def record_key(self, key: str, modifiers: List[str] = None):
        """Manually record key press"""
        self._add_delay_if_needed()
        action = KeyAction(key, modifiers)
        self.script.add_action(action)
        self.last_action_time = datetime.now()

    def add_delay(self, milliseconds: int):
        """Manually add delay"""
        action = DelayAction(milliseconds)
        self.script.add_action(action)

    def _add_delay_if_needed(self):
        """Add delay if enough time has passed"""
        if self.last_action_time and len(self.script.actions) > 0:
            elapsed = (datetime.now() - self.last_action_time).total_seconds() * 1000
            if elapsed > self.min_delay_ms:
                self.add_delay(int(elapsed))


def _create_action_from_dict(data: Dict) -> Optional[AutomationAction]:
    """Create action from dictionary"""
    action_type = data.get('type')

    if action_type == 'click':
        return ClickAction(**{k: v for k, v in data.items() if k != 'type'})
    elif action_type == 'type':
        return TypeAction(**{k: v for k, v in data.items() if k != 'type'})
    elif action_type == 'key':
        return KeyAction(**{k: v for k, v in data.items() if k != 'type'})
    elif action_type == 'delay':
        return DelayAction(**{k: v for k, v in data.items() if k != 'type'})
    elif action_type == 'screenshot':
        return ScreenshotAction(**{k: v for k, v in data.items() if k != 'type'})
    elif action_type == 'setvar':
        return SetVariableAction(**{k: v for k, v in data.items() if k != 'type'})

    return None


# Convenience functions
def record(script_name: str = "My Script") -> Recorder:
    """Start recording a new script"""
    recorder = Recorder()
    recorder.start(script_name)
    return recorder


def load_script(filepath: str) -> AutomationScript:
    """Load an automation script"""
    return AutomationScript.load(filepath)


def create_script(name: str = "New Script") -> AutomationScript:
    """Create a new automation script"""
    return AutomationScript(name)
