"""Thread-safe in-memory storage for extracted API collections."""

import threading
from dataclasses import dataclass, field
from datetime import datetime, timezone
from uuid import uuid4

from maeris_mcp.types.api import APISummary, ExtractedAPI, ExtractedAPICollection, StoredAPIList


@dataclass
class CreateApisForSession:
    """Active session for LLM-guided create_apis_for workflow."""

    session_id: str
    functionality: str
    collection_name: str
    target_path: str
    scan_root: str
    base_for_rel: str
    file_list: list[str]
    apis: list[ExtractedAPI] = field(default_factory=list)
    http_clients: set[str] = field(default_factory=set)
    api_fingerprints: set[str] = field(default_factory=set)
    created_at: str = ""
    status: str = "in_progress"


@dataclass
class ApiScanSession:
    """Active scan session for LLM-guided API extraction."""

    scan_id: str
    project_name: str
    target_path: str
    scan_root: str
    base_for_rel: str
    file_list: list[str]
    apis: list[ExtractedAPI] = field(default_factory=list)
    http_clients: set[str] = field(default_factory=set)
    api_fingerprints: set[str] = field(default_factory=set)
    created_at: str = ""
    status: str = "in_progress"    # "in_progress" | "completed"
    storage_id: str | None = None  # set after finalization


class APIStore:
    """Thread-safe storage for extracted API collections."""

    def __init__(self) -> None:
        self._lock = threading.RLock()
        self._api_lists: dict[str, StoredAPIList] = {}
        self._sessions: dict[str, ApiScanSession] = {}
        self._for_sessions: dict[str, CreateApisForSession] = {}

    def store(self, name: str, collection: ExtractedAPICollection) -> str:
        """Store an API collection and return its ID."""
        with self._lock:
            storage_id = str(uuid4())
            now = datetime.now(timezone.utc).isoformat()
            self._api_lists[storage_id] = StoredAPIList(
                id=storage_id,
                name=name,
                collection=collection,
                stored_at=now,
                last_accessed=now,
            )
            return storage_id

    def get(self, storage_id: str) -> StoredAPIList | None:
        """Retrieve an API collection by ID."""
        with self._lock:
            if api_list := self._api_lists.get(storage_id):
                api_list.last_accessed = datetime.now(timezone.utc).isoformat()
                return api_list
            return None

    def get_all(self) -> list[StoredAPIList]:
        """Return all stored API collections."""
        with self._lock:
            return list(self._api_lists.values())

    def delete(self, storage_id: str) -> bool:
        """Delete an API collection by ID."""
        with self._lock:
            if storage_id in self._api_lists:
                del self._api_lists[storage_id]
                return True
            return False

    def clear(self) -> int:
        """Clear all stored API collections, return count deleted."""
        with self._lock:
            count = len(self._api_lists)
            self._api_lists.clear()
            return count

    def find_by_project_name(self, project_name: str) -> list[StoredAPIList]:
        """Find API lists by project name."""
        with self._lock:
            return [
                api_list
                for api_list in self._api_lists.values()
                if api_list.collection and api_list.collection.project_name == project_name
            ]

    # --- Scan session management ---

    def create_scan_session(
        self,
        project_name: str,
        target_path: str,
        scan_root: str,
        base_for_rel: str,
        file_list: list[str],
    ) -> str:
        """Create a new API scan session and return its scan_id."""
        with self._lock:
            scan_id = str(uuid4())
            self._sessions[scan_id] = ApiScanSession(
                scan_id=scan_id,
                project_name=project_name,
                target_path=target_path,
                scan_root=scan_root,
                base_for_rel=base_for_rel,
                file_list=file_list,
                created_at=datetime.now(timezone.utc).isoformat(),
            )
            return scan_id

    def get_session(self, scan_id: str) -> ApiScanSession | None:
        """Retrieve a scan session by ID."""
        with self._lock:
            return self._sessions.get(scan_id)

    def add_apis(self, scan_id: str, apis: list[ExtractedAPI]) -> dict[str, int]:
        """Add APIs to a session, deduplicating by method+endpoint fingerprint."""
        with self._lock:
            session = self._sessions.get(scan_id)
            if not session:
                return {"added": 0, "deduped": 0}
            added = 0
            deduped = 0
            for api in apis:
                fingerprint = f"{api.method}|{api.endpoint}|{api.name}"
                if fingerprint in session.api_fingerprints:
                    deduped += 1
                    continue
                session.api_fingerprints.add(fingerprint)
                session.apis.append(api)
                added += 1
            return {"added": added, "deduped": deduped}

    def add_http_clients(self, scan_id: str, clients: list[str]) -> None:
        """Merge HTTP client names into the session."""
        with self._lock:
            session = self._sessions.get(scan_id)
            if session:
                session.http_clients.update(clients)

    def finalize_session(self, scan_id: str) -> tuple[str, ExtractedAPICollection] | None:
        """Finalize a scan session: build collection, store it, and return (storage_id, collection)."""
        with self._lock:
            session = self._sessions.get(scan_id)
            if not session:
                return None

            apis = session.apis
            by_method: dict[str, int] = {}
            by_auth_type: dict[str, int] = {}
            endpoints: set[str] = set()
            for api in apis:
                by_method[api.method] = by_method.get(api.method, 0) + 1
                auth_type = api.auth.type if api.auth else "none"
                by_auth_type[str(auth_type)] = by_auth_type.get(str(auth_type), 0) + 1
                endpoints.add(api.endpoint)

            summary = APISummary(
                total_apis=len(apis),
                by_method=by_method,
                by_auth_type=by_auth_type,
                unique_endpoints=len(endpoints),
            )
            collection = ExtractedAPICollection(
                project_name=session.project_name,
                root_path=session.target_path,
                apis=apis,
                extraction_time=datetime.now(timezone.utc).isoformat(),
                files_analyzed=list(session.file_list),
                http_clients=sorted(session.http_clients) if session.http_clients else None,
                summary=summary,
            )
            storage_id = self.store(session.project_name, collection)
            session.status = "completed"
            session.storage_id = storage_id
            return storage_id, collection

    # --- CreateApisFor session management ---

    def create_for_session(
        self,
        functionality: str,
        collection_name: str,
        target_path: str,
        scan_root: str,
        base_for_rel: str,
        file_list: list[str],
    ) -> str:
        """Create a new create_apis_for session and return its session_id."""
        with self._lock:
            session_id = str(uuid4())
            self._for_sessions[session_id] = CreateApisForSession(
                session_id=session_id,
                functionality=functionality,
                collection_name=collection_name,
                target_path=target_path,
                scan_root=scan_root,
                base_for_rel=base_for_rel,
                file_list=file_list,
                created_at=datetime.now(timezone.utc).isoformat(),
            )
            return session_id

    def get_for_session(self, session_id: str) -> CreateApisForSession | None:
        """Retrieve a create_apis_for session by ID."""
        with self._lock:
            return self._for_sessions.get(session_id)

    def add_for_apis(
        self,
        session_id: str,
        apis: list[ExtractedAPI],
        http_clients: list[str] | None = None,
    ) -> dict[str, int]:
        """Add APIs to a create_apis_for session, deduplicating by method+endpoint+name."""
        with self._lock:
            session = self._for_sessions.get(session_id)
            if not session:
                return {"added": 0, "deduped": 0}
            added = 0
            deduped = 0
            for api in apis:
                fingerprint = f"{api.method}|{api.endpoint}|{api.name}"
                if fingerprint in session.api_fingerprints:
                    deduped += 1
                    continue
                session.api_fingerprints.add(fingerprint)
                session.apis.append(api)
                added += 1
            if http_clients:
                session.http_clients.update(c for c in http_clients if isinstance(c, str) and c.strip())
            return {"added": added, "deduped": deduped}

    def finalize_for_session(self, session_id: str) -> CreateApisForSession | None:
        """Mark a create_apis_for session as completed and return it."""
        with self._lock:
            session = self._for_sessions.get(session_id)
            if not session:
                return None
            session.status = "completed"
            return session
