"""
Genesis Trigger Memory System

Tier 4 of the Five-Tier Cognitive Memory Architecture.
Trigger Memory stores condition → action mappings as n8n workflows.

Key Insight: n8n IS the trigger memory execution layer.
- Creating workflow = Creating trigger memory
- Deleting workflow = Forgetting trigger
- Workflow execution = Trigger activation

Usage:
    from trigger_memory import TriggerMemory

    tm = TriggerMemory()

    # Create a trigger
    trigger_id = tm.create_trigger(
        name="revenue_alert",
        condition={"metric": "daily_revenue", "operator": "<", "threshold": 100},
        action={"type": "notification", "channel": "slack", "message": "Low revenue alert!"}
    )

    # List triggers
    triggers = tm.list_triggers()

    # Check trigger
    tm.check_trigger(trigger_id, current_value=50)  # Would fire
"""

import json
import os
from datetime import datetime
from typing import Dict, List, Optional, Any
from dataclasses import dataclass, asdict
from pathlib import Path

# Try to import n8n MCP tools if available
try:
    from mcp import ClientSession
    MCP_AVAILABLE = True
except ImportError:
    MCP_AVAILABLE = False


@dataclass
class TriggerCondition:
    """Defines when a trigger should fire."""
    metric: str
    operator: str  # <, >, ==, !=, contains, regex
    threshold: Any
    duration_seconds: Optional[int] = None  # Must be true for this long

    def evaluate(self, current_value: Any) -> bool:
        """Evaluate if condition is met."""
        ops = {
            '<': lambda a, b: a < b,
            '>': lambda a, b: a > b,
            '<=': lambda a, b: a <= b,
            '>=': lambda a, b: a >= b,
            '==': lambda a, b: a == b,
            '!=': lambda a, b: a != b,
            'contains': lambda a, b: b in str(a),
            'not_contains': lambda a, b: b not in str(a),
        }

        if self.operator not in ops:
            return False

        return ops[self.operator](current_value, self.threshold)


@dataclass
class TriggerAction:
    """Defines what happens when trigger fires."""
    action_type: str  # notification, webhook, workflow, memory_update
    target: str  # Where to send/what to call
    parameters: Dict[str, Any] = None

    def __post_init__(self):
        if self.parameters is None:
            self.parameters = {}


@dataclass
class Trigger:
    """A complete trigger memory entry."""
    id: str
    name: str
    description: str
    condition: TriggerCondition
    action: TriggerAction
    enabled: bool = True
    created_at: str = None
    last_triggered: Optional[str] = None
    trigger_count: int = 0
    n8n_workflow_id: Optional[str] = None  # Links to n8n workflow

    def __post_init__(self):
        if self.created_at is None:
            self.created_at = datetime.utcnow().isoformat()


class TriggerMemory:
    """
    Manages Trigger Memory (Tier 4) - condition → action mappings.

    Integrates with n8n as the execution layer:
    - Local triggers: Stored in JSON, evaluated in Python
    - n8n triggers: Stored as workflows, executed by n8n
    """

    def __init__(self, storage_path: str = None, n8n_host: str = None, n8n_api_key: str = None):
        """
        Initialize Trigger Memory.

        Args:
            storage_path: Path to local trigger storage (JSON file)
            n8n_host: n8n instance URL (optional, enables n8n integration)
            n8n_api_key: n8n API key (optional)
        """
        self.storage_path = Path(storage_path or "/mnt/e/genesis-system/genesis-memory/triggers.json")
        self.n8n_host = n8n_host or os.environ.get("N8N_HOST")
        self.n8n_api_key = n8n_api_key or os.environ.get("N8N_API_KEY")

        self.triggers: Dict[str, Trigger] = {}
        self._load_triggers()

    def _load_triggers(self):
        """Load triggers from storage."""
        if self.storage_path.exists():
            try:
                data = json.loads(self.storage_path.read_text())
                for trigger_data in data.get("triggers", []):
                    trigger = self._dict_to_trigger(trigger_data)
                    self.triggers[trigger.id] = trigger
            except (json.JSONDecodeError, KeyError) as e:
                print(f"Warning: Could not load triggers: {e}")
                self.triggers = {}

    def _save_triggers(self):
        """Save triggers to storage."""
        self.storage_path.parent.mkdir(parents=True, exist_ok=True)
        data = {
            "version": "1.0",
            "updated_at": datetime.utcnow().isoformat(),
            "triggers": [self._trigger_to_dict(t) for t in self.triggers.values()]
        }
        self.storage_path.write_text(json.dumps(data, indent=2))

    def _trigger_to_dict(self, trigger: Trigger) -> Dict:
        """Convert Trigger to dictionary."""
        return {
            "id": trigger.id,
            "name": trigger.name,
            "description": trigger.description,
            "condition": asdict(trigger.condition),
            "action": asdict(trigger.action),
            "enabled": trigger.enabled,
            "created_at": trigger.created_at,
            "last_triggered": trigger.last_triggered,
            "trigger_count": trigger.trigger_count,
            "n8n_workflow_id": trigger.n8n_workflow_id
        }

    def _dict_to_trigger(self, data: Dict) -> Trigger:
        """Convert dictionary to Trigger."""
        return Trigger(
            id=data["id"],
            name=data["name"],
            description=data.get("description", ""),
            condition=TriggerCondition(**data["condition"]),
            action=TriggerAction(**data["action"]),
            enabled=data.get("enabled", True),
            created_at=data.get("created_at"),
            last_triggered=data.get("last_triggered"),
            trigger_count=data.get("trigger_count", 0),
            n8n_workflow_id=data.get("n8n_workflow_id")
        )

    def _generate_id(self, name: str) -> str:
        """Generate unique trigger ID."""
        import hashlib
        base = f"{name}_{datetime.utcnow().isoformat()}"
        return f"trigger_{hashlib.md5(base.encode()).hexdigest()[:12]}"

    # ========== Core Operations ==========

    def create_trigger(
        self,
        name: str,
        condition: Dict[str, Any],
        action: Dict[str, Any],
        description: str = "",
        sync_to_n8n: bool = False
    ) -> str:
        """
        Create a new trigger memory entry.

        Args:
            name: Human-readable trigger name
            condition: Dict with metric, operator, threshold
            action: Dict with action_type, target, parameters
            description: Optional description
            sync_to_n8n: If True, create corresponding n8n workflow

        Returns:
            Trigger ID
        """
        trigger_id = self._generate_id(name)

        trigger = Trigger(
            id=trigger_id,
            name=name,
            description=description,
            condition=TriggerCondition(**condition),
            action=TriggerAction(**action)
        )

        # Optionally sync to n8n
        if sync_to_n8n and self.n8n_host:
            workflow_id = self._create_n8n_workflow(trigger)
            trigger.n8n_workflow_id = workflow_id

        self.triggers[trigger_id] = trigger
        self._save_triggers()

        return trigger_id

    def get_trigger(self, trigger_id: str) -> Optional[Trigger]:
        """Get a trigger by ID."""
        return self.triggers.get(trigger_id)

    def list_triggers(self, enabled_only: bool = False) -> List[Trigger]:
        """List all triggers."""
        triggers = list(self.triggers.values())
        if enabled_only:
            triggers = [t for t in triggers if t.enabled]
        return triggers

    def update_trigger(self, trigger_id: str, updates: Dict[str, Any]) -> bool:
        """Update a trigger's properties."""
        if trigger_id not in self.triggers:
            return False

        trigger = self.triggers[trigger_id]

        for key, value in updates.items():
            if key == "condition" and isinstance(value, dict):
                trigger.condition = TriggerCondition(**value)
            elif key == "action" and isinstance(value, dict):
                trigger.action = TriggerAction(**value)
            elif hasattr(trigger, key):
                setattr(trigger, key, value)

        self._save_triggers()
        return True

    def delete_trigger(self, trigger_id: str, delete_n8n_workflow: bool = True) -> bool:
        """
        Delete a trigger (forget).

        Args:
            trigger_id: Trigger to delete
            delete_n8n_workflow: Also delete linked n8n workflow
        """
        if trigger_id not in self.triggers:
            return False

        trigger = self.triggers[trigger_id]

        # Delete n8n workflow if exists
        if delete_n8n_workflow and trigger.n8n_workflow_id:
            self._delete_n8n_workflow(trigger.n8n_workflow_id)

        del self.triggers[trigger_id]
        self._save_triggers()
        return True

    def enable_trigger(self, trigger_id: str) -> bool:
        """Enable a trigger."""
        return self.update_trigger(trigger_id, {"enabled": True})

    def disable_trigger(self, trigger_id: str) -> bool:
        """Disable a trigger."""
        return self.update_trigger(trigger_id, {"enabled": False})

    # ========== Evaluation ==========

    def check_trigger(self, trigger_id: str, current_value: Any) -> bool:
        """
        Check if a trigger should fire given current value.

        Returns True if condition is met and trigger fires.
        """
        trigger = self.triggers.get(trigger_id)
        if not trigger or not trigger.enabled:
            return False

        if trigger.condition.evaluate(current_value):
            self._fire_trigger(trigger)
            return True

        return False

    def check_all_triggers(self, metrics: Dict[str, Any]) -> List[str]:
        """
        Check all triggers against provided metrics.

        Args:
            metrics: Dict of metric_name -> current_value

        Returns:
            List of trigger IDs that fired
        """
        fired = []

        for trigger in self.triggers.values():
            if not trigger.enabled:
                continue

            metric_value = metrics.get(trigger.condition.metric)
            if metric_value is not None:
                if trigger.condition.evaluate(metric_value):
                    self._fire_trigger(trigger)
                    fired.append(trigger.id)

        return fired

    def _fire_trigger(self, trigger: Trigger):
        """Execute trigger action and update stats."""
        trigger.last_triggered = datetime.utcnow().isoformat()
        trigger.trigger_count += 1

        # Execute action based on type
        action = trigger.action

        if action.action_type == "notification":
            self._execute_notification(action)
        elif action.action_type == "webhook":
            self._execute_webhook(action)
        elif action.action_type == "workflow" and trigger.n8n_workflow_id:
            self._execute_n8n_workflow(trigger.n8n_workflow_id, action.parameters)
        elif action.action_type == "memory_update":
            self._execute_memory_update(action)

        self._save_triggers()

    # ========== Action Executors ==========

    def _execute_notification(self, action: TriggerAction):
        """Send notification (placeholder - implement per channel)."""
        print(f"[NOTIFICATION] {action.target}: {action.parameters.get('message', '')}")

    def _execute_webhook(self, action: TriggerAction):
        """Call webhook URL."""
        import urllib.request
        import urllib.error

        try:
            data = json.dumps(action.parameters).encode('utf-8')
            req = urllib.request.Request(
                action.target,
                data=data,
                headers={'Content-Type': 'application/json'}
            )
            urllib.request.urlopen(req, timeout=30)
        except urllib.error.URLError as e:
            print(f"[WEBHOOK ERROR] {action.target}: {e}")

    def _execute_memory_update(self, action: TriggerAction):
        """Update memory system (placeholder)."""
        print(f"[MEMORY UPDATE] {action.target}: {action.parameters}")

    # ========== n8n Integration ==========

    def _create_n8n_workflow(self, trigger: Trigger) -> Optional[str]:
        """Create n8n workflow from trigger definition."""
        if not self.n8n_host or not self.n8n_api_key:
            return None

        # Build n8n workflow JSON
        workflow = {
            "name": f"Genesis Trigger: {trigger.name}",
            "nodes": [
                {
                    "parameters": {},
                    "name": "Start",
                    "type": "n8n-nodes-base.manualTrigger",
                    "position": [250, 300]
                },
                {
                    "parameters": {
                        "url": trigger.action.target,
                        "options": {}
                    },
                    "name": "Execute Action",
                    "type": "n8n-nodes-base.httpRequest",
                    "position": [450, 300]
                }
            ],
            "connections": {
                "Start": {
                    "main": [[{"node": "Execute Action", "type": "main", "index": 0}]]
                }
            },
            "settings": {},
            "tags": [{"name": "genesis-trigger"}]
        }

        # Call n8n API to create workflow
        try:
            import urllib.request

            url = f"{self.n8n_host}/api/v1/workflows"
            data = json.dumps(workflow).encode('utf-8')
            req = urllib.request.Request(
                url,
                data=data,
                headers={
                    'Content-Type': 'application/json',
                    'X-N8N-API-KEY': self.n8n_api_key
                },
                method='POST'
            )

            response = urllib.request.urlopen(req, timeout=30)
            result = json.loads(response.read())
            return result.get('id')

        except Exception as e:
            print(f"[N8N ERROR] Could not create workflow: {e}")
            return None

    def _delete_n8n_workflow(self, workflow_id: str) -> bool:
        """Delete n8n workflow."""
        if not self.n8n_host or not self.n8n_api_key:
            return False

        try:
            import urllib.request

            url = f"{self.n8n_host}/api/v1/workflows/{workflow_id}"
            req = urllib.request.Request(
                url,
                headers={'X-N8N-API-KEY': self.n8n_api_key},
                method='DELETE'
            )
            urllib.request.urlopen(req, timeout=30)
            return True

        except Exception as e:
            print(f"[N8N ERROR] Could not delete workflow: {e}")
            return False

    def _execute_n8n_workflow(self, workflow_id: str, parameters: Dict = None) -> bool:
        """Execute n8n workflow."""
        if not self.n8n_host or not self.n8n_api_key:
            return False

        try:
            import urllib.request

            url = f"{self.n8n_host}/api/v1/workflows/{workflow_id}/execute"
            data = json.dumps(parameters or {}).encode('utf-8')
            req = urllib.request.Request(
                url,
                data=data,
                headers={
                    'Content-Type': 'application/json',
                    'X-N8N-API-KEY': self.n8n_api_key
                },
                method='POST'
            )
            urllib.request.urlopen(req, timeout=60)
            return True

        except Exception as e:
            print(f"[N8N ERROR] Could not execute workflow: {e}")
            return False

    def sync_from_n8n(self) -> int:
        """
        Sync triggers from n8n workflows tagged with 'genesis-trigger'.

        Returns number of triggers synced.
        """
        if not self.n8n_host or not self.n8n_api_key:
            return 0

        try:
            import urllib.request

            url = f"{self.n8n_host}/api/v1/workflows?tags=genesis-trigger"
            req = urllib.request.Request(
                url,
                headers={'X-N8N-API-KEY': self.n8n_api_key}
            )

            response = urllib.request.urlopen(req, timeout=30)
            workflows = json.loads(response.read())

            synced = 0
            for wf in workflows.get('data', []):
                # Check if we already have this trigger
                existing = [t for t in self.triggers.values()
                           if t.n8n_workflow_id == wf['id']]

                if not existing:
                    # Create trigger from workflow
                    trigger_id = self._generate_id(wf['name'])
                    trigger = Trigger(
                        id=trigger_id,
                        name=wf['name'].replace('Genesis Trigger: ', ''),
                        description=f"Synced from n8n workflow {wf['id']}",
                        condition=TriggerCondition(
                            metric="n8n_manual",
                            operator="==",
                            threshold=True
                        ),
                        action=TriggerAction(
                            action_type="workflow",
                            target=wf['id']
                        ),
                        n8n_workflow_id=wf['id']
                    )
                    self.triggers[trigger_id] = trigger
                    synced += 1

            if synced > 0:
                self._save_triggers()

            return synced

        except Exception as e:
            print(f"[N8N ERROR] Could not sync workflows: {e}")
            return 0

    # ========== Statistics ==========

    def get_stats(self) -> Dict[str, Any]:
        """Get trigger memory statistics."""
        triggers = list(self.triggers.values())

        return {
            "total_triggers": len(triggers),
            "enabled_triggers": len([t for t in triggers if t.enabled]),
            "disabled_triggers": len([t for t in triggers if not t.enabled]),
            "n8n_linked": len([t for t in triggers if t.n8n_workflow_id]),
            "total_fires": sum(t.trigger_count for t in triggers),
            "most_active": max(triggers, key=lambda t: t.trigger_count).name if triggers else None
        }


# ========== Convenience Functions ==========

def create_revenue_alert(
    tm: TriggerMemory,
    threshold: float = 100,
    duration_days: int = 3
) -> str:
    """Create a revenue alert trigger."""
    return tm.create_trigger(
        name="revenue_below_threshold",
        description=f"Alert when daily revenue < ${threshold} for {duration_days} days",
        condition={
            "metric": "daily_revenue",
            "operator": "<",
            "threshold": threshold,
            "duration_seconds": duration_days * 86400
        },
        action={
            "action_type": "notification",
            "target": "slack",
            "parameters": {
                "channel": "#alerts",
                "message": f"Revenue alert: Below ${threshold}/day for {duration_days} days"
            }
        }
    )


def create_lead_scorer(
    tm: TriggerMemory,
    high_value_threshold: int = 70
) -> str:
    """Create a high-value lead trigger."""
    return tm.create_trigger(
        name="high_value_lead",
        description=f"Trigger when lead score > {high_value_threshold}",
        condition={
            "metric": "lead_score",
            "operator": ">",
            "threshold": high_value_threshold
        },
        action={
            "action_type": "workflow",
            "target": "high_value_lead_workflow",
            "parameters": {
                "priority": "high",
                "auto_assign": True
            }
        },
        sync_to_n8n=True
    )


if __name__ == "__main__":
    # Test the trigger memory system
    print("Testing Trigger Memory System...")

    tm = TriggerMemory()

    # Create test triggers
    print("\n1. Creating triggers...")

    revenue_trigger = tm.create_trigger(
        name="daily_revenue_check",
        description="Alert when revenue drops below $100",
        condition={
            "metric": "daily_revenue",
            "operator": "<",
            "threshold": 100
        },
        action={
            "action_type": "notification",
            "target": "console",
            "parameters": {"message": "Low revenue alert!"}
        }
    )
    print(f"   Created: {revenue_trigger}")

    lead_trigger = tm.create_trigger(
        name="high_value_lead",
        description="Process high-scoring leads",
        condition={
            "metric": "lead_score",
            "operator": ">",
            "threshold": 70
        },
        action={
            "action_type": "webhook",
            "target": "https://example.com/webhook",
            "parameters": {"priority": "high"}
        }
    )
    print(f"   Created: {lead_trigger}")

    # List triggers
    print("\n2. Listing triggers...")
    for t in tm.list_triggers():
        print(f"   - {t.name} ({t.id}): {t.condition.metric} {t.condition.operator} {t.condition.threshold}")

    # Test trigger evaluation
    print("\n3. Testing trigger evaluation...")

    metrics = {
        "daily_revenue": 50,  # Below threshold - should fire
        "lead_score": 85      # Above threshold - should fire
    }

    fired = tm.check_all_triggers(metrics)
    print(f"   Fired triggers: {fired}")

    # Show stats
    print("\n4. Statistics:")
    stats = tm.get_stats()
    for key, value in stats.items():
        print(f"   {key}: {value}")

    print("\nTrigger Memory System test complete!")
