"""Thread-safe in-memory storage for API automation setup sessions."""

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


@dataclass
class AutomationSession:
    """Active session for the setup_api_automation tool."""

    session_id: str
    target_path: str
    base_for_rel: str
    file_list: list[str]
    status: str  # "scanning" | "review" | "completed"
    apis: list[dict] = field(default_factory=list)
    flows: list[dict] = field(default_factory=list)
    environments: list[dict] = field(default_factory=list)
    api_fingerprints: set[str] = field(default_factory=set)
    feedback_history: list[str] = field(default_factory=list)
    excluded_api_names: set[str] = field(default_factory=set)
    excluded_flow_names: set[str] = field(default_factory=set)
    excluded_flow_steps: set[tuple[str, str]] = field(default_factory=set)
    excluded_env_names: set[str] = field(default_factory=set)
    excluded_env_fields: set[tuple[str, str]] = field(default_factory=set)
    created_at: str = ""


class SetupAutomationStore:
    """Thread-safe storage for setup automation sessions."""

    def __init__(self) -> None:
        self._lock = threading.RLock()
        self._sessions: dict[str, AutomationSession] = {}

    def create_session(
        self,
        target_path: str,
        base_for_rel: str,
        file_list: list[str],
    ) -> str:
        """Create a new automation session and return its session_id."""
        with self._lock:
            session_id = str(uuid4())
            self._sessions[session_id] = AutomationSession(
                session_id=session_id,
                target_path=target_path,
                base_for_rel=base_for_rel,
                file_list=file_list,
                status="scanning",
                created_at=datetime.now(timezone.utc).isoformat(),
            )
            return session_id

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

    def add_apis(self, session_id: str, apis: list[dict]) -> dict[str, int]:
        """Add APIs to a session, deduplicating by method+endpoint+name fingerprint."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return {"added": 0, "deduped": 0}
            added = 0
            deduped = 0
            for api in apis:
                method = (api.get("method") or "").upper()
                endpoint = api.get("endpoint") or api.get("url") or ""
                name = api.get("name") or ""
                fingerprint = f"{method}|{endpoint}|{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 finalize_scan(
        self,
        session_id: str,
        flows: list[dict],
        environments: list[dict],
    ) -> bool:
        """Transition session from scanning to review phase."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return False
            session.flows = flows or []
            session.environments = environments or []
            session.status = "review"
            return True

    def update_review(
        self,
        session_id: str,
        updated_apis: list[dict] | None,
        updated_flows: list[dict] | None,
        updated_environments: list[dict] | None,
        feedback: str = "",
    ) -> tuple[list[str], bool]:
        """Apply updates during review phase. Returns (changes_applied, success)."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return [], False

            changes: list[str] = []

            if updated_apis is not None:
                old_names = {a.get("name") for a in session.apis}
                new_names = {a.get("name") for a in updated_apis}
                for name in sorted(old_names - new_names):
                    changes.append(f"Removed API: {name}")
                for name in sorted(new_names - old_names):
                    changes.append(f"Added API: {name}")
                old_by_name = {a.get("name"): a for a in session.apis}
                new_by_name = {a.get("name"): a for a in updated_apis}
                for name in sorted(old_names & new_names):
                    if old_by_name.get(name) != new_by_name.get(name):
                        changes.append(f"Updated API: {name}")
                session.apis = updated_apis
                session.api_fingerprints = set()
                for api in updated_apis:
                    method = (api.get("method") or "").upper()
                    endpoint = api.get("endpoint") or api.get("url") or ""
                    name = api.get("name") or ""
                    session.api_fingerprints.add(f"{method}|{endpoint}|{name}")

            if updated_flows is not None:
                old_flow_names = {f.get("name") for f in session.flows}
                new_flow_names = {f.get("name") for f in updated_flows}
                for name in sorted(old_flow_names - new_flow_names):
                    changes.append(f"Removed flow: {name}")
                for name in sorted(new_flow_names - old_flow_names):
                    changes.append(f"Added flow: {name}")
                old_flows_by_name = {f.get("name"): f for f in session.flows}
                new_flows_by_name = {f.get("name"): f for f in updated_flows}
                for name in sorted(old_flow_names & new_flow_names):
                    if old_flows_by_name.get(name) != new_flows_by_name.get(name):
                        changes.append(f"Updated flow: {name}")
                session.flows = updated_flows

            if updated_environments is not None:
                old_env_names = {e.get("name") for e in session.environments}
                new_env_names = {e.get("name") for e in updated_environments}
                for name in sorted(old_env_names - new_env_names):
                    changes.append(f"Removed environment: {name}")
                for name in sorted(new_env_names - old_env_names):
                    changes.append(f"Added environment: {name}")
                session.environments = updated_environments

            if feedback:
                session.feedback_history.append(feedback)

            if not changes:
                changes.append("No changes detected")

            return changes, True

    def apply_toggles(
        self,
        session_id: str,
        exclude_apis: list[str] | None = None,
        include_apis: list[str] | None = None,
        exclude_flows: list[str] | None = None,
        include_flows: list[str] | None = None,
        exclude_flow_steps: list[dict] | None = None,
        include_flow_steps: list[dict] | None = None,
        exclude_environments: list[str] | None = None,
        include_environments: list[str] | None = None,
        exclude_env_fields: list[dict] | None = None,
        include_env_fields: list[dict] | None = None,
    ) -> tuple[list[str], bool]:
        """Toggle inclusion state for named APIs, flows, flow steps, environments, and env fields."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return [], False

            changes: list[str] = []

            if exclude_apis:
                for name in exclude_apis:
                    if name not in session.excluded_api_names:
                        session.excluded_api_names.add(name)
                        changes.append(f"Deselected API: {name}")

            if include_apis:
                for name in include_apis:
                    if name in session.excluded_api_names:
                        session.excluded_api_names.discard(name)
                        changes.append(f"Re-selected API: {name}")

            if exclude_flows:
                for name in exclude_flows:
                    if name not in session.excluded_flow_names:
                        session.excluded_flow_names.add(name)
                        changes.append(f"Deselected flow: {name}")

            if include_flows:
                for name in include_flows:
                    if name in session.excluded_flow_names:
                        session.excluded_flow_names.discard(name)
                        changes.append(f"Re-selected flow: {name}")

            if exclude_flow_steps:
                for step in exclude_flow_steps:
                    if isinstance(step, dict):
                        flow_name = step.get("flow_name", "")
                        api_name = step.get("api_name", "")
                        if flow_name and api_name:
                            key = (flow_name, api_name)
                            if key not in session.excluded_flow_steps:
                                session.excluded_flow_steps.add(key)
                                changes.append(f"Deselected step {api_name} from {flow_name}")

            if include_flow_steps:
                for step in include_flow_steps:
                    if isinstance(step, dict):
                        flow_name = step.get("flow_name", "")
                        api_name = step.get("api_name", "")
                        if flow_name and api_name:
                            key = (flow_name, api_name)
                            if key in session.excluded_flow_steps:
                                session.excluded_flow_steps.discard(key)
                                changes.append(f"Re-selected step {api_name} from {flow_name}")

            if exclude_environments:
                for name in exclude_environments:
                    if name not in session.excluded_env_names:
                        session.excluded_env_names.add(name)
                        changes.append(f"Deselected environment: {name}")

            if include_environments:
                for name in include_environments:
                    if name in session.excluded_env_names:
                        session.excluded_env_names.discard(name)
                        changes.append(f"Re-selected environment: {name}")

            if exclude_env_fields:
                for item in exclude_env_fields:
                    if isinstance(item, dict):
                        env_name = item.get("env_name", "")
                        field_name = item.get("field_name", "")
                        if env_name and field_name:
                            key = (env_name, field_name)
                            if key not in session.excluded_env_fields:
                                session.excluded_env_fields.add(key)
                                changes.append(f"Deselected env field: {env_name}.{field_name}")

            if include_env_fields:
                for item in include_env_fields:
                    if isinstance(item, dict):
                        env_name = item.get("env_name", "")
                        field_name = item.get("field_name", "")
                        if env_name and field_name:
                            key = (env_name, field_name)
                            if key in session.excluded_env_fields:
                                session.excluded_env_fields.discard(key)
                                changes.append(f"Re-selected env field: {env_name}.{field_name}")

            return changes, True

    def set_env_field_values(
        self,
        session_id: str,
        values: list[dict],
    ) -> tuple[list[str], bool]:
        """Set actual values for specific (env_name, field_name) pairs."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return [], False

            changes: list[str] = []
            for v in values:
                if not isinstance(v, dict):
                    continue
                env_name = v.get("env_name", "")
                field_name = v.get("field_name", "")
                field_value = v.get("field_value", "")
                if not env_name or not field_name:
                    continue
                for env in session.environments:
                    if env.get("name") == env_name:
                        for f in env.get("fields") or []:
                            if f.get("field_name") == field_name:
                                f["field_value"] = field_value
                                changes.append(f"Set {env_name}.{field_name} = {field_value!r}")

            return changes, True

    def mark_completed(self, session_id: str) -> bool:
        """Mark session as completed after saving to Maeris."""
        with self._lock:
            session = self._sessions.get(session_id)
            if not session:
                return False
            session.status = "completed"
            return True
