"""Voice domain — data access."""
from __future__ import annotations

from typing import Optional
from uuid import UUID, uuid4

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from src.domain.voice.models import ProjectPhoneNumber, ProjectVoiceAgent, ProjectVoiceConfig


def _digits(value: str) -> str:
    return "".join(ch for ch in value if ch.isdigit())


class VoiceRepository:
    def __init__(self, db: AsyncSession) -> None:
        self._db = db

    async def get_config(self, project_id: UUID) -> Optional[ProjectVoiceConfig]:
        result = await self._db.execute(
            select(ProjectVoiceConfig).where(ProjectVoiceConfig.project_id == project_id)
        )
        return result.scalar_one_or_none()

    async def upsert_config(self, project_id: UUID, **fields) -> ProjectVoiceConfig:
        row = await self.get_config(project_id)
        if not row:
            row = ProjectVoiceConfig(id=uuid4(), project_id=project_id, **fields)
            self._db.add(row)
        else:
            for k, v in fields.items():
                setattr(row, k, v)
        await self._db.flush()
        return row

    async def list_agents(self, project_id: UUID) -> list[ProjectVoiceAgent]:
        result = await self._db.execute(
            select(ProjectVoiceAgent)
            .where(ProjectVoiceAgent.project_id == project_id)
            .order_by(ProjectVoiceAgent.name)
        )
        return list(result.scalars().all())

    async def get_agent(self, project_id: UUID, agent_id: UUID) -> Optional[ProjectVoiceAgent]:
        result = await self._db.execute(
            select(ProjectVoiceAgent).where(
                ProjectVoiceAgent.project_id == project_id,
                ProjectVoiceAgent.id == agent_id,
            )
        )
        return result.scalar_one_or_none()

    async def upsert_agent(self, project_id: UUID, agent_id: Optional[UUID], **fields) -> ProjectVoiceAgent:
        row = None
        if agent_id:
            row = await self.get_agent(project_id, agent_id)
        if not row:
            row = ProjectVoiceAgent(id=agent_id or uuid4(), project_id=project_id, **fields)
            self._db.add(row)
        else:
            for k, v in fields.items():
                setattr(row, k, v)
        await self._db.flush()
        return row

    async def list_numbers(self, project_id: UUID) -> list[ProjectPhoneNumber]:
        result = await self._db.execute(
            select(ProjectPhoneNumber)
            .where(ProjectPhoneNumber.project_id == project_id)
            .order_by(ProjectPhoneNumber.e164)
        )
        return list(result.scalars().all())

    async def get_number_by_e164(self, project_id: UUID, e164: str) -> Optional[ProjectPhoneNumber]:
        wanted = _digits(e164)
        result = await self._db.execute(
            select(ProjectPhoneNumber).where(ProjectPhoneNumber.project_id == project_id)
        )
        for row in result.scalars().all():
            if _digits(row.e164) == wanted or row.e164 == e164:
                return row
        return None

    async def upsert_number(self, project_id: UUID, number_id: Optional[UUID], **fields) -> ProjectPhoneNumber:
        row = None
        if number_id:
            result = await self._db.execute(
                select(ProjectPhoneNumber).where(
                    ProjectPhoneNumber.project_id == project_id,
                    ProjectPhoneNumber.id == number_id,
                )
            )
            row = result.scalar_one_or_none()
        if not row:
            row = ProjectPhoneNumber(id=number_id or uuid4(), project_id=project_id, **fields)
            self._db.add(row)
        else:
            for k, v in fields.items():
                setattr(row, k, v)
        await self._db.flush()
        return row
