"""Email domains domain — data access layer."""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

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

from src.domain.email_domains.models import (
    WorkspaceEmailDomain,
    WorkspaceEmailOverageCharge,
    WorkspaceEmailQuota,
)


class EmailDomainRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def get_by_id(self, domain_id: UUID) -> Optional[WorkspaceEmailDomain]:
        result = await self._db.execute(
            select(WorkspaceEmailDomain).where(WorkspaceEmailDomain.id == domain_id)
        )
        return result.scalar_one_or_none()

    async def get_by_workspace(self, workspace_id: UUID) -> Optional[WorkspaceEmailDomain]:
        result = await self._db.execute(
            select(WorkspaceEmailDomain).where(WorkspaceEmailDomain.workspace_id == workspace_id)
        )
        return result.scalar_one_or_none()

    async def get_by_domain(self, domain: str) -> Optional[WorkspaceEmailDomain]:
        result = await self._db.execute(
            select(WorkspaceEmailDomain).where(WorkspaceEmailDomain.domain == domain.strip().lower())
        )
        return result.scalar_one_or_none()

    async def create(
        self,
        workspace_id: UUID,
        domain: str,
        *,
        dkim_tokens: list[str],
        configuration_set_name: str,
        created_by: Optional[UUID] = None,
    ) -> WorkspaceEmailDomain:
        d = WorkspaceEmailDomain(
            workspace_id=workspace_id,
            domain=domain.strip().lower(),
            dkim_tokens=list(dkim_tokens),
            ses_configuration_set_name=configuration_set_name,
            created_by=created_by,
        )
        self._db.add(d)
        await self._db.flush()
        await self._db.refresh(d)
        return d

    async def mark_verified(self, domain_id: UUID) -> Optional[WorkspaceEmailDomain]:
        d = await self.get_by_id(domain_id)
        if not d:
            return None
        d.status = "verified"
        d.verified_at = datetime.now(timezone.utc)
        d.failure_reason = None
        await self._db.flush()
        return d

    async def mark_failed(self, domain_id: UUID, reason: str) -> Optional[WorkspaceEmailDomain]:
        d = await self.get_by_id(domain_id)
        if not d:
            return None
        d.status = "failed"
        d.failure_reason = reason
        await self._db.flush()
        return d

    async def set_mail_from(
        self, domain_id: UUID, *, mail_from_domain: str, status: str
    ) -> Optional[WorkspaceEmailDomain]:
        d = await self.get_by_id(domain_id)
        if not d:
            return None
        d.mail_from_domain = mail_from_domain
        d.mail_from_status = status
        await self._db.flush()
        return d

    async def delete(self, domain_id: UUID) -> None:
        d = await self.get_by_id(domain_id)
        if d:
            await self._db.delete(d)
            await self._db.flush()


class EmailQuotaRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def get_for_update(self, workspace_id: UUID) -> Optional[WorkspaceEmailQuota]:
        """Row-lock the quota row for the atomic check-and-increment in EmailQuotaService."""
        result = await self._db.execute(
            select(WorkspaceEmailQuota)
            .where(WorkspaceEmailQuota.workspace_id == workspace_id)
            .with_for_update()
        )
        return result.scalar_one_or_none()

    async def get(self, workspace_id: UUID) -> Optional[WorkspaceEmailQuota]:
        result = await self._db.execute(
            select(WorkspaceEmailQuota).where(WorkspaceEmailQuota.workspace_id == workspace_id)
        )
        return result.scalar_one_or_none()

    async def create(self, workspace_id: UUID, **kwargs) -> WorkspaceEmailQuota:
        q = WorkspaceEmailQuota(workspace_id=workspace_id, **kwargs)
        self._db.add(q)
        await self._db.flush()
        await self._db.refresh(q)
        return q

    async def set_override(
        self,
        workspace_id: UUID,
        *,
        included_monthly: Optional[int],
        daily_cap: Optional[int],
        rate_per_1000_minor: Optional[int],
        reason: str,
        approved_by: UUID,
    ) -> WorkspaceEmailQuota:
        q = await self.get(workspace_id)
        if not q:
            q = await self.create(
                workspace_id,
                period_start=datetime.now(timezone.utc),
                period_end=datetime.now(timezone.utc),
                included_monthly=0,
                daily_cap=0,
                day_bucket_date=datetime.now(timezone.utc).date(),
            )
        if included_monthly is not None:
            q.override_included_monthly = included_monthly
        if daily_cap is not None:
            q.override_daily_cap = daily_cap
        if rate_per_1000_minor is not None:
            q.override_rate_per_1000_minor = rate_per_1000_minor
        q.override_reason = reason
        q.override_approved_by = approved_by
        q.override_approved_at = datetime.now(timezone.utc)
        await self._db.flush()
        return q

    async def list_with_pending_overage(self) -> list[WorkspaceEmailQuota]:
        result = await self._db.execute(
            select(WorkspaceEmailQuota).where(WorkspaceEmailQuota.pending_overage_units > 0)
        )
        return list(result.scalars().all())


class EmailOverageChargeRepository:
    def __init__(self, db: AsyncSession):
        self._db = db

    async def create(
        self,
        workspace_id: UUID,
        *,
        period_start: datetime,
        period_end: datetime,
        overage_units: int,
        rate_per_1000_minor: int,
        amount_minor: int,
        stripe_invoice_item_id: Optional[str],
    ) -> WorkspaceEmailOverageCharge:
        c = WorkspaceEmailOverageCharge(
            workspace_id=workspace_id,
            period_start=period_start,
            period_end=period_end,
            overage_units=overage_units,
            rate_per_1000_minor=rate_per_1000_minor,
            amount_minor=amount_minor,
            stripe_invoice_item_id=stripe_invoice_item_id,
        )
        self._db.add(c)
        await self._db.flush()
        await self._db.refresh(c)
        return c
