"""Proxy lease repository — typed CRUD via BaseRepository.

Manages the link between proxy servers and platform accounts with support
for sticky (permanent) and timed (auto-reclaim) lease modes.
"""

from __future__ import annotations

import logging
import uuid
from datetime import datetime, timezone
from typing import Any

from repo_core import BaseRepository, hook
from repo_core.domains.proxy.model import ProxyLease

logger = logging.getLogger(__name__)


class ProxyLeaseRepository(BaseRepository[ProxyLease]):
    """CRUD for ``proxy_leases`` via BaseRepository backends.

    Usage::

        repo = ProxyLeaseRepository(model=ProxyLease)
        async with repo:
            lease = await repo.create_lease({...})
            active = await repo.find_active_leases_by_account("x", account_uuid)
    """

    TABLE = "proxy_leases"

    # -- Hooks -------------------------------------------------------------

    @hook("after_create")
    async def _log_create(self, lease: ProxyLease) -> None:
        logger.debug(
            "Proxy lease created: id=%s proxy=%s account=%s/%s mode=%s",
            lease.id, lease.proxy_id,
            lease.account_type, lease.account_id,
            lease.lease_mode,
        )

    @hook("after_update")
    async def _log_update(self, lease: ProxyLease) -> None:
        logger.debug("Proxy lease updated: id=%s", lease.id)

    # -- CRUD --------------------------------------------------------------

    async def create_lease(self, data: dict[str, Any]) -> ProxyLease:
        """Create a new proxy-to-account lease.

        For timed leases, ``expires_at`` is computed automatically from
        ``assigned_at`` + ``lease_duration_seconds``.
        """
        data.setdefault("assigned_at", datetime.now(timezone.utc))
        if data.get("lease_mode") == "timed" and data.get("lease_duration_seconds"):
            assigned = data["assigned_at"]
            if isinstance(assigned, str):
                assigned = datetime.fromisoformat(assigned)
            data["expires_at"] = assigned.timestamp() + data["lease_duration_seconds"]
            data["expires_at"] = datetime.fromtimestamp(data["expires_at"], tz=timezone.utc)

        await self.emit("before_create", data)
        created = await self.insert_one(self.TABLE, data, returning=["*"])
        await self.emit("after_create", created)
        return created

    async def get_by_id(self, lease_id: uuid.UUID) -> ProxyLease | None:
        return await self.find_by_id(self.TABLE, lease_id)

    async def find_active_leases_by_account(
        self, account_type: str, account_id: uuid.UUID
    ) -> list[ProxyLease]:
        """Return all active leases for a given account."""
        return await self.find(
            self.TABLE,
            {
                "account_type": account_type,
                "account_id": account_id,
                "is_active": True,
            },
            order_by=["-assigned_at"],
        )

    async def find_active_leases_by_proxy(
        self, proxy_id: str
    ) -> list[ProxyLease]:
        """Return all active leases for a given proxy."""
        return await self.find(
            self.TABLE,
            {"proxy_id": proxy_id, "is_active": True},
            order_by=["-assigned_at"],
        )

    async def find_expired_timed_leases(self) -> list[ProxyLease]:
        """Return all active timed leases that have passed their expiry."""
        now = datetime.now(timezone.utc)
        return await self.find(
            self.TABLE,
            {
                "lease_mode": "timed",
                "is_active": True,
                "expires_at": {"$lte": now.isoformat()},
            },
        )

    async def reclaim(self, lease_id: uuid.UUID) -> ProxyLease | None:
        """Reclaim a proxy — mark lease inactive and set reclaimed_at."""
        updated = await self.update_by_id(
            self.TABLE,
            lease_id,
            {
                "is_active": False,
                "reclaimed_at": datetime.now(timezone.utc),
            },
        )
        if updated is not None:
            await self.emit("after_update", updated)
        return updated

    async def list_leases_by_proxy(
        self, proxy_id: str, limit: int = 20, offset: int = 0
    ) -> list[ProxyLease]:
        return await self.find(
            self.TABLE,
            {"proxy_id": proxy_id},
            limit=limit,
            offset=offset,
            order_by=["-assigned_at"],
        )
