"""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 deactivate_leases_for_account(
        self, account_id: uuid.UUID,
    ) -> int:
        """Mark all active leases for *account_id* as inactive and reclaimed.

        Returns the number of leases deactivated.  This should be called
        whenever an account is deleted or its proxy is reassigned, so
        that stale lease rows don't block proxy rotation logic.
        """
        now = datetime.now(timezone.utc)
        active = await self.find(
            self.TABLE,
            {"account_id": account_id, "is_active": True},
            limit=1000,
            offset=0,
        )
        count = 0
        for lease in active:
            updated = await self.update_by_id(
                self.TABLE,
                lease.id,
                {
                    "is_active": False,
                    "reclaimed_at": now,
                },
            )
            if updated is not None:
                await self.emit("after_update", updated)
                count += 1
        if count:
            logger.info(
                "Deactivated %d proxy lease(s) for account %s",
                count, account_id,
            )
        return count

    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"],
        )
