"""Voice domain — Core owns agents, numbers, entitlements; Cloud executes."""
from __future__ import annotations

from typing import Optional
from uuid import UUID

from src.domain.voice.models import ProjectVoiceAgent, ProjectVoiceConfig
from src.domain.voice.repository import VoiceRepository
from src.domain.voice.schemas import (
    PhoneNumberResponse,
    UpdateVoiceConfigRequest,
    UpsertPhoneNumberRequest,
    UpsertVoiceAgentRequest,
    VoiceAgentResponse,
    VoiceConfigResponse,
    VoiceInboundResolution,
)
from src.integrations.cloud_control_client import CloudControlClient


class VoiceService:
    def __init__(self, *, repo: VoiceRepository, cloud_client: CloudControlClient) -> None:
        self._repo = repo
        self._cloud = cloud_client

    async def get_or_create_config(self, project_id: UUID, workspace_id: UUID) -> VoiceConfigResponse:
        cfg = await self._repo.get_config(project_id)
        if not cfg:
            cfg = await self._repo.upsert_config(project_id)
        await self._push_config(workspace_id, cfg)
        return VoiceConfigResponse.model_validate(cfg)

    async def update_config(
        self,
        project_id: UUID,
        workspace_id: UUID,
        body: UpdateVoiceConfigRequest,
    ) -> VoiceConfigResponse:
        fields = body.model_dump(exclude_unset=True)
        cfg = await self._repo.upsert_config(project_id, **fields)
        await self._push_config(workspace_id, cfg)
        return VoiceConfigResponse.model_validate(cfg)

    async def list_agents(self, project_id: UUID) -> list[VoiceAgentResponse]:
        rows = await self._repo.list_agents(project_id)
        return [VoiceAgentResponse.model_validate(r) for r in rows]

    async def upsert_agent(
        self,
        project_id: UUID,
        workspace_id: UUID,
        body: UpsertVoiceAgentRequest,
        agent_id: Optional[UUID] = None,
    ) -> VoiceAgentResponse:
        row = await self._repo.upsert_agent(project_id, agent_id, **body.model_dump())
        await self._cloud.push_voice_agent(
            project_id=str(project_id),
            workspace_id=str(workspace_id),
            agent={
                "id": str(row.id),
                "project_id": str(project_id),
                "workspace_id": str(workspace_id),
                "name": row.name,
                "version": row.version,
                "provider": row.provider,
                "model": row.model,
                "voice": row.voice,
                "instructions": row.instructions,
                "tools": row.tools_json or [],
                "transfer_targets": row.transfer_targets_json or [],
                "secret_ref": row.secret_ref,
                "status": row.status,
            },
        )
        return VoiceAgentResponse.model_validate(row)

    async def list_numbers(self, project_id: UUID) -> list[PhoneNumberResponse]:
        rows = await self._repo.list_numbers(project_id)
        return [PhoneNumberResponse.model_validate(r) for r in rows]

    async def upsert_number(
        self,
        project_id: UUID,
        workspace_id: UUID,
        body: UpsertPhoneNumberRequest,
        number_id: Optional[UUID] = None,
    ) -> PhoneNumberResponse:
        row = await self._repo.upsert_number(project_id, number_id, **body.model_dump())
        await self._cloud.push_voice_phone_number(
            project_id=str(project_id),
            workspace_id=str(workspace_id),
            number={
                "id": str(row.id),
                "project_id": str(project_id),
                "workspace_id": str(workspace_id),
                "e164": row.e164,
                "provider": row.provider,
                "provider_sid": row.provider_sid,
                "inbound_agent_id": str(row.inbound_agent_id) if row.inbound_agent_id else None,
                "outbound_agent_id": str(row.outbound_agent_id) if row.outbound_agent_id else None,
                "status": row.status,
            },
        )
        return PhoneNumberResponse.model_validate(row)

    async def resolve_inbound(self, project_id: UUID, to_number: Optional[str]) -> VoiceInboundResolution:
        cfg = await self._repo.get_config(project_id)
        agent: Optional[ProjectVoiceAgent] = None
        phone_id: Optional[str] = None
        if to_number:
            number = await self._repo.get_number_by_e164(project_id, to_number)
            if number:
                phone_id = str(number.id)
                if number.inbound_agent_id:
                    agent = await self._repo.get_agent(project_id, number.inbound_agent_id)
        if agent is None and cfg and cfg.default_agent_id:
            agent = await self._repo.get_agent(project_id, cfg.default_agent_id)
        if agent is None:
            agents = await self._repo.list_agents(project_id)
            agent = next((a for a in agents if a.status == "active"), None)
        billing_mode = (cfg.billing_mode if cfg else "managed") or "managed"
        credential_ref = cfg.credential_ref if cfg else None
        byok = billing_mode == "byok"
        return VoiceInboundResolution(
            agent_id=str(agent.id) if agent else None,
            agent_config_version=agent.version if agent else None,
            phone_number_id=phone_id,
            voice_provider=(agent.provider if agent else "openai_realtime"),
            model=agent.model if agent else None,
            voice=agent.voice if agent else None,
            instructions=agent.instructions if agent else None,
            max_call_seconds=3600,
            max_concurrent_calls=cfg.max_concurrent_calls if cfg else 1,
            tools=list(agent.tools_json or []) if agent else [],
            transfer_targets=list(agent.transfer_targets_json or []) if agent else [],
            secret_ref=(credential_ref or (agent.secret_ref if agent else None)) if byok else None,
            billing_mode=billing_mode,
            credential_ref=credential_ref if byok else None,
            usage_product="voice",
        )

    async def _push_config(self, workspace_id: UUID, cfg: ProjectVoiceConfig) -> None:
        await self._cloud.push_voice_config(
            project_id=str(cfg.project_id),
            workspace_id=str(workspace_id),
            enabled=cfg.enabled,
            max_concurrent_calls=cfg.max_concurrent_calls,
            default_agent_id=str(cfg.default_agent_id) if cfg.default_agent_id else None,
            billing_mode=cfg.billing_mode,
            credential_ref=cfg.credential_ref,
        )
