"""Session management for open-webview-e2e CLI."""

from __future__ import annotations

import json
import uuid
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Dict, List, Optional

from nomo_e2e import WebViewController


SESSION_STATE_FILE = Path.home() / ".open-webview-e2e" / "sessions.json"
@dataclass
class SessionInfo:
    """Session information."""

    id: str
    name: str
    endpoint: str
    driver: str
    url: Optional[str] = None
    title: Optional[str] = None
    created_at: Optional[datetime] = None
    last_active: Optional[datetime] = None
    is_active: bool = True

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "id": self.id,
            "name": self.name,
            "endpoint": self.endpoint,
            "driver": self.driver,
            "url": self.url,
            "title": self.title,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "last_active": self.last_active.isoformat() if self.last_active else None,
            "is_active": self.is_active,
        }

    @classmethod
    def from_dict(cls, data: dict) -> SessionInfo:
        """Create from dictionary."""
        data = data.copy()
        if data.get("created_at"):
            data["created_at"] = datetime.fromisoformat(data["created_at"])
        if data.get("last_active"):
            data["last_active"] = datetime.fromisoformat(data["last_active"])
        return cls(**data)


class Session:
    """WebView session wrapper."""

    def __init__(self, name: str, endpoint: str, driver: str = "auto"):
        self.id = str(uuid.uuid4())[:8]
        self.name = name or f"session-{self.id}"
        self.endpoint = endpoint
        self.driver = driver
        self.controller: Optional[WebViewController] = None
        self.created_at = datetime.now()
        self.last_active = datetime.now()

    def connect(self) -> WebViewController:
        """Connect to WebView."""
        self.controller = WebViewController.connect(self.endpoint, self.driver)
        self.last_active = datetime.now()
        return self.controller

    def disconnect(self) -> None:
        """Disconnect from WebView."""
        if self.controller:
            self.controller.disconnect()
            self.controller = None
        self.last_active = datetime.now()

    @property
    def is_connected(self) -> bool:
        """Check if session is connected."""
        return self.controller is not None

    @property
    def url(self) -> Optional[str]:
        """Get current URL."""
        if self.controller:
            return self.controller.url
        return None

    @property
    def title(self) -> Optional[str]:
        """Get page title."""
        if self.controller:
            return self.controller.title
        return None

    def get_info(self) -> SessionInfo:
        """Get session information."""
        return SessionInfo(
            id=self.id,
            name=self.name,
            endpoint=self.endpoint,
            driver=self.driver,
            url=self.url,
            title=self.title,
            created_at=self.created_at,
            last_active=self.last_active,
            is_active=self.is_connected,
        )

    def save_state(self, path: str) -> dict:
        """Save session state to file.

        Saves cookies and localStorage for session persistence.

        Returns:
            State dictionary with cookies, localStorage, and session info.
        """

        state = {
            "session": self.get_info().to_dict(),
            "cookies": [],
            "localStorage": {},
            "sessionStorage": {},
        }

        if self.controller:
            try:
                state["localStorage"] = self.controller.execute_script("""
                    () => {
                        const items = {};
                        try {
                            for (let i = 0; i < localStorage.length; i++) {
                                const key = localStorage.key(i);
                                items[key] = localStorage.getItem(key);
                            }
                        } catch (e) {}
                        return items;
                    }
                """) or {}

                state["sessionStorage"] = self.controller.execute_script("""
                    () => {
                        const items = {};
                        try {
                            for (let i = 0; i < sessionStorage.length; i++) {
                                const key = sessionStorage.key(i);
                                items[key] = sessionStorage.getItem(key);
                            }
                        } catch (e) {}
                        return items;
                    }
                """) or {}
            except Exception as e:
                print(f"Warning: Failed to save storage state: {e}")

        with open(path, "w", encoding="utf-8") as f:
            json.dump(state, f, indent=2)

        return state

    def load_state(self, path: str) -> None:
        """Load session state from file.

        Restores cookies and localStorage.

        Args:
            path: Path to state file.
        """
        with open(path, "r", encoding="utf-8") as f:
            state = json.load(f)

        if self.controller:
            try:
                if "localStorage" in state and state["localStorage"]:
                    self.controller.execute_script("() => localStorage.clear()")
                    for key, value in state["localStorage"].items():
                        self.controller.execute_script(
                            "(key, value) => localStorage.setItem(key, value)",
                            key, value
                        )

                if "sessionStorage" in state and state["sessionStorage"]:
                    self.controller.execute_script("() => sessionStorage.clear()")
                    for key, value in state["sessionStorage"].items():
                        self.controller.execute_script(
                            "(key, value) => sessionStorage.setItem(key, value)",
                            key, value
                        )
            except Exception as e:
                print(f"Warning: Failed to load storage state: {e}")


class SessionManager:
    """Global session manager with file-based persistence."""

    _instance: Optional[SessionManager] = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._sessions = {}
            cls._instance._current: Optional[str] = None
            cls._instance._load_from_file()
        return cls._instance

    def __init__(self):
        self._sessions: Dict[str, Session] = {}
        self._current: Optional[str] = None
        self._load_from_file()

    def _get_state_file(self) -> Path:
        """Get session state file path."""
        state_file = SESSION_STATE_FILE
        state_file.parent.mkdir(parents=True, exist_ok=True)
        return state_file

    def _load_from_file(self) -> None:
        """Load sessions from file."""
        state_file = self._get_state_file()
        if not state_file.exists():
            return

        try:
            with open(state_file, "r", encoding="utf-8") as f:
                data = json.load(f)
                self._current = data.get("current")

                for name, session_data in data.get("sessions", {}).items():
                    session = Session(
                        name=name,
                        endpoint=session_data["endpoint"],
                        driver=session_data["driver"]
                    )
                    session.id = session_data.get("id", session.id)
                    self._sessions[name] = session
        except Exception as e:
            print(f"Warning: Failed to load sessions from file: {e}")

    def _save_to_file(self) -> None:
        """Save sessions to file."""
        state_file = self._get_state_file()

        data = {
            "current": self._current,
            "sessions": {
                name: {
                    "id": session.id,
                    "endpoint": session.endpoint,
                    "driver": session.driver,
                }
                for name, session in self._sessions.items()
            }
        }

        try:
            with open(state_file, "w", encoding="utf-8") as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            print(f"Warning: Failed to save sessions to file: {e}")

    def create_session(self, name: str, endpoint: str, driver: str = "auto") -> Session:
        """Create a new session."""
        if name in self._sessions:
            old_session = self._sessions[name]
            old_session.disconnect()

        session = Session(name, endpoint, driver)
        session.connect()
        self._sessions[name] = session
        self._current = name
        self._save_to_file()
        return session

    def get_session(self, name: Optional[str] = None) -> Optional[Session]:
        """Get session by name."""
        if name is None:
            name = self._current
        return self._sessions.get(name)

    def get_current_session(self) -> Optional[Session]:
        """Get current active session."""
        return self.get_session(self._current)

    def require_current_session(self) -> Session:
        """Get current session or raise error. Auto-reconnect if needed."""
        session = self.get_current_session()
        if session is None:
            raise RuntimeError("No active session. Use 'open' command first.")

        if not session.is_connected:
            print(f"Reconnecting to {session.endpoint}...")
            try:
                session.connect()
            except Exception as e:
                raise RuntimeError(f"Failed to reconnect to session '{session.name}': {e}")

        return session

    def list_sessions(self) -> List[SessionInfo]:
        """List all sessions."""
        return [s.get_info() for s in self._sessions.values()]

    def close_session(self, name: Optional[str] = None) -> None:
        """Close a session."""
        if name is None:
            name = self._current
        if name and name in self._sessions:
            self._sessions[name].disconnect()
            del self._sessions[name]
            if self._current == name:
                self._current = next(iter(self._sessions), None)
            self._save_to_file()

    def close_all(self) -> None:
        """Close all sessions."""
        for session in list(self._sessions.values()):
            session.disconnect()
        self._sessions.clear()
        self._current = None
        self._save_to_file()

    def switch_session(self, name: str) -> None:
        """Switch to a different session."""
        if name not in self._sessions:
            raise ValueError(f"Session '{name}' not found.")
        self._current = name
        self._save_to_file()

    def reset(self) -> None:
        """Reset session manager."""
        self.close_all()
        self._sessions = {}
        self._current = None


_manager: Optional[SessionManager] = None


def get_session_manager() -> SessionManager:
    """Get global session manager instance."""
    global _manager
    if _manager is None:
        _manager = SessionManager()
    return _manager
