"""Workspace lifecycle management service."""

from __future__ import annotations

import logging
import os
import shutil
import uuid
from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING

from sqlalchemy import select

from src.models.workspace import Workspace
from src.services.git_service import GitService

if TYPE_CHECKING:
    from sqlalchemy.ext.asyncio import AsyncSession

    from src.models.git_repository import GitRepository
    from src.models.tool_instance import ToolInstance

logger = logging.getLogger(__name__)


@dataclass
class SyncResult:
    """Result of a workspace sync operation."""

    branch_deleted: bool = False


class WorkspaceHasInstancesError(Exception):
    """Raised when attempting to delete a workspace with running instances."""

    def __init__(self, instances: list[ToolInstance]) -> None:
        self.instances = instances
        super().__init__(f"Workspace has {len(instances)} running tool instance(s)")


class WorkspaceManager:
    """Manages workspace lifecycle: create, delete, sync, validate."""

    BASE_PATH = "/data/working-copies"

    def _workspace_path(self, repo_id: uuid.UUID, name: str) -> str:
        """Return the filesystem path for a workspace."""
        return os.path.join(self.BASE_PATH, str(repo_id), name)

    async def create(
        self,
        repo: GitRepository,
        user_id: uuid.UUID,
        name: str,
        branch: str = "main",
    ) -> Workspace:
        """Clone repo to workspace path and create DB record.

        Args:
            repo: The git repository to clone.
            user_id: The owner user ID.
            name: The workspace name (unique per repo).
            branch: The branch to clone (default: "main").

        Returns:
            The created Workspace record.

        Raises:
            RuntimeError: If git clone fails.
        """
        path = self._workspace_path(repo.id, name)
        os.makedirs(os.path.dirname(path), exist_ok=True)

        logger.info(
            "Creating workspace: name=%s, repo=%s, branch=%s", name, repo.id, branch
        )

        if not repo.remote_url:
            raise ValueError("Repository has no remote URL")

        await GitService.clone(repo.remote_url, branch, path)

        workspace = Workspace(
            name=name,
            repo_id=repo.id,
            user_id=user_id,
            branch=branch,
            path=path,
            status="ready",
            last_sync_at=datetime.now(),
        )
        logger.info("Workspace created: %s", workspace.id)
        return workspace

    async def delete(
        self,
        workspace: Workspace,
        force: bool = False,
        session: AsyncSession | None = None,
    ) -> None:
        """Delete a workspace and all associated tool instances.

        Args:
            workspace: The workspace to delete.
            force: If True, delete even if instances exist.
            session: The database session (required for checking instances).

        Raises:
            WorkspaceHasInstancesError: If instances exist and force=False.
        """
        if session is None:
            raise ValueError("session is required for delete")

        instances = await self._get_instances(workspace, session)
        if instances and not force:
            raise WorkspaceHasInstancesError(instances)

        # Stop and delete all instances
        for instance in instances:
            await self._stop_and_delete_instance(instance)

        # Delete directory
        if os.path.exists(workspace.path):
            shutil.rmtree(workspace.path, ignore_errors=True)
            logger.info("Deleted workspace directory: %s", workspace.path)

        # Delete record
        await session.delete(workspace)
        logger.info("Deleted workspace record: %s", workspace.id)

    async def sync(self, workspace: Workspace) -> SyncResult:
        """Sync a workspace with its remote.

        Args:
            workspace: The workspace to sync.

        Returns:
            SyncResult indicating whether the branch was deleted.

        Raises:
            RuntimeError: If git operations fail.
        """
        logger.info("Syncing workspace: %s", workspace.id)

        await GitService.fetch(workspace.path)

        if not GitService.branch_exists_remotely(workspace.path, workspace.branch):
            return SyncResult(branch_deleted=True)

        await GitService.pull(workspace.path, workspace.branch)
        workspace.last_sync_at = datetime.now()
        logger.info("Workspace synced: %s", workspace.id)
        return SyncResult(branch_deleted=False)

    async def _get_instances(
        self,
        workspace: Workspace,
        session: AsyncSession,
    ) -> list[ToolInstance]:
        """Get all tool instances associated with this workspace."""
        from src.models.tool_instance import ToolInstance

        result = await session.execute(
            select(ToolInstance).where(ToolInstance.workspace_id == workspace.id)
        )
        return list(result.scalars().all())

    async def _stop_and_delete_instance(self, instance: ToolInstance) -> None:
        """Stop and delete a tool instance.

        TODO(PR-2): Wire up to actual instance stop/delete logic.
        For now, this is a placeholder.
        """
        logger.warning("Placeholder: stopping and deleting instance %s", instance.id)
