"""Mint a desktop execution envelope when a paired device claims a queued local run."""
from __future__ import annotations
from datetime import datetime, timezone
from typing import Any
from uuid import UUID

from src.config import get_settings
from src.core.exceptions import ConflictError, NotFoundError, ValidationError
from src.core.security.jwt import RUN_GRANT_TTL_MINUTES, create_run_grant_token
from src.domain.codegen.actions_pipeline import (
    _sign_payload,
    build_codegen_actions_runner,
)
from src.domain.codegen.repository import CodegenRunRepository
from src.domain.devices.models import Device
from src.domain.devices.repository import DeviceRepository
from src.domain.projects.repository import ProjectRepository


class DeviceClaimService:
    def __init__(
        self,
        *,
        runs: CodegenRunRepository,
        devices: DeviceRepository,
        projects: ProjectRepository,
        db_factory,
    ) -> None:
        self._runs = runs
        self._devices = devices
        self._projects = projects
        self._db_factory = db_factory

    async def claim(self, *, device: Device, run_id: UUID) -> dict[str, Any]:
        run = await self._runs.get_by_id(run_id)
        if not run or run.device_id != device.id or run.execution_target != "local":
            raise NotFoundError("Run not found for this device")
        project = await self._projects.get_with_repo(run.project_id)
        if not project:
            raise ValidationError("Project not found")
        workspace_id = project.workspace_id

        settings = get_settings()
        grant = create_run_grant_token(device.id, run.id, run.project_id, workspace_id)
        callback_url = f"{settings.api_url.rstrip('/')}/api/internal/codegen/actions/{run.id}/complete"
        progress_url = f"{settings.api_url.rstrip('/')}/api/internal/codegen/actions/{run.id}/progress"
        source_bundle_url = f"{settings.api_url.rstrip('/')}/api/internal/codegen/actions/{run.id}/source-bundle"
        result_upload_url = f"{settings.api_url.rstrip('/')}/api/internal/codegen/actions/{run.id}/bundle-complete"

        plan = dict(run.plan or {})
        provider = settings.ai_provider.value if hasattr(settings.ai_provider, "value") else str(settings.ai_provider)
        repo_full_name = project.repo.repo_full_name if getattr(project, "repo", None) else ""
        preview_branch = (
            getattr(project.repo, "preview_branch", None) if getattr(project, "repo", None) else None
        ) or settings.hosting_preview_branch or "preview"

        payload = {
            "run_id": str(run.id),
            "project_id": str(run.project_id),
            "provider": provider,
            "branch": preview_branch,
            "callback_url": callback_url,
            "progress_url": progress_url,
            "system_prompt": plan.get("system_prompt") or "",
            "user_prompt": plan.get("user_prompt") or "",
            "project_type": getattr(project, "type", "") or "",
            "project_ref": (getattr(project, "project_ref", None) or "").strip(),
            "context": dict(plan.get("context") or {}),
            "commit_message": f"p2hs: apply codegen run {str(run.id)[:8]}",
            "git_committer_name": settings.git_committer_name,
            "git_committer_email": "noreply@p2hs.com",
            "cursor_model": settings.cursor_model,
            "cursor_stream_progress": bool(settings.cursor_agent_stream_progress),
            "openai_model": settings.openai_model,
            "claude_model": settings.claude_model,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        envelope = {
            "payload": payload,
            "signature": _sign_payload(payload, settings.codegen_actions_payload_secret or ""),
        }

        env: dict[str, str] = {
            "P2HS_CODEGEN_CALLBACK_TOKEN": grant,
            "P2HS_CODEGEN_PAYLOAD_SECRET": settings.codegen_actions_payload_secret or "",
        }

        claimed = await self._runs.try_claim_for_device(run_id, device.id)
        if claimed is None:
            raise ConflictError("Run is not queued for claim")
        await self._devices.set_last_workspace(device, workspace_id)

        return {
            "run_id": claimed.id,
            "project_id": claimed.project_id,
            "workspace_id": workspace_id,
            "run_grant": grant,
            "run_grant_expires_in": RUN_GRANT_TTL_MINUTES * 60,
            "git": {
                "clone_url": "",
                "branch": preview_branch,
                "repo_full_name": repo_full_name,
                "expires_in": 0,
            },
            "repo_path_hint": str(claimed.project_id),
            "callback_url": callback_url,
            "progress_url": progress_url,
            "source_bundle_url": source_bundle_url,
            "result_upload_url": result_upload_url,
            "env": env,
            "payload_envelope": envelope,
            "runner_source": build_codegen_actions_runner(),
        }
