"""Obol session - context manager that tracks all agent operations."""
import hashlib
import os
import time
import uuid
from typing import Optional

from obol.api import ObolAPI
from obol.interceptors import install_interceptors, uninstall_interceptors
from obol.pipeline import EventPipeline
from obol.local_log import LocalAuditLog


class ObolSession:
    """Tracked session that monitors all agent I/O.

    Usage:
        with obol.session(agent) as session:
            # All file/network/process ops are tracked
            result = agent.run(task)

            if session.can("read", "credentials.db"):
                load_creds()
    """

    def __init__(self, api: ObolAPI, agent: dict, policies: dict = None, **kwargs):
        self.api = api
        self.agent = agent
        self.agent_id = agent["id"]
        self.session_id = str(uuid.uuid4())[:16]
        self.policies = policies or {}
        self._pipeline = EventPipeline(api, self.agent_id, self.session_id)
        self._local_log = LocalAuditLog(self.agent_id, self.session_id)
        self._active = False
        self._event_count = 0

    def __enter__(self):
        self._active = True
        self._pipeline.start()
        self._local_log.start()
        install_interceptors(self._on_action)
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._active = False
        uninstall_interceptors()
        self._pipeline.flush()
        self._pipeline.stop()
        self._local_log.stop()
        return False

    def _on_action(self, action: str, target: str, content: bytes = None,
                   result: str = "success", duration_ms: int = None):
        """Called by interceptors for every monitored operation."""
        if not self._active:
            return

        self._event_count += 1

        # Compute content metadata (privacy-preserving)
        target_hash = None
        content_size = None
        content_type = None
        detected_patterns = []

        if content is not None:
            if isinstance(content, str):
                content = content.encode()
            target_hash = hashlib.sha256(content).hexdigest()
            content_size = len(content)
            content_type = _guess_content_type(target)
            detected_patterns = _detect_patterns(content)

        # Queue event for async send to Obol cloud (metadata only)
        self._pipeline.record({
            "action": action,
            "target": target,
            "target_hash": target_hash,
            "content_size": content_size,
            "content_type": content_type,
            "result": result,
            "duration_ms": duration_ms,
            "detected_patterns": detected_patterns,
        })

        # Write full event to local audit log (includes content hash, not content)
        self._local_log.write({
            "action": action,
            "target": target,
            "target_hash": target_hash,
            "content_size": content_size,
            "result": result,
            "duration_ms": duration_ms,
        })

    def can(self, action: str, resource: str) -> bool:
        """Check if agent has sufficient trust for this action."""
        try:
            result = self.api.check_access(self.agent_id, action, resource)
            return result.get("allowed", False)
        except Exception:
            # Fail open if API unreachable (configurable)
            return True

    def check_access(self, action: str, resource: str) -> bool:
        """Alias for can() - check if agent has sufficient trust."""
        return self.can(action, resource)

    def end(self):
        """Explicitly end this session. Alternative to using as context manager."""
        self.__exit__(None, None, None)

    def record(self, action: str, target: str = None, result: str = "success",
               duration_ms: int = None, content: bytes = None):
        """Manually record an event within this session."""
        self._on_action(action, target, content, result, duration_ms)

    @property
    def event_count(self) -> int:
        return self._event_count


def _guess_content_type(target: str) -> Optional[str]:
    if not target:
        return None
    ext_map = {
        ".py": "text/x-python", ".js": "text/javascript", ".json": "application/json",
        ".html": "text/html", ".css": "text/css", ".md": "text/markdown",
        ".txt": "text/plain", ".csv": "text/csv", ".xml": "application/xml",
        ".yaml": "text/yaml", ".yml": "text/yaml", ".pdf": "application/pdf",
        ".png": "image/png", ".jpg": "image/jpeg", ".gif": "image/gif",
    }
    for ext, mime in ext_map.items():
        if target.lower().endswith(ext):
            return mime
    return "application/octet-stream"


def _detect_patterns(content: bytes) -> list[str]:
    """Detect data pattern types in content (flags only, not values)."""
    import re

    patterns = []
    try:
        text = content.decode("utf-8", errors="ignore")
    except Exception:
        return patterns

    checks = [
        ("email", r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"),
        ("phone", r"\b\d{3}[-.]?\d{3}[-.]?\d{4}\b"),
        ("api_key", r"(?:api[_-]?key|token|secret)[=:]\s*['\"]?\w{16,}"),
        ("ip_address", r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b"),
        ("credit_card", r"\b\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}\b"),
        ("ssn", r"\b\d{3}-\d{2}-\d{4}\b"),
        ("private_key", r"-----BEGIN (?:RSA |EC )?PRIVATE KEY-----"),
        ("password", r"(?:password|passwd|pwd)[=:]\s*\S+"),
    ]

    for name, pattern in checks:
        if re.search(pattern, text, re.IGNORECASE):
            patterns.append(name)

    return patterns
