"""Proxy repository — typed CRUD via BaseRepository.

All persistence is delegated to :class:`BaseRepository` methods — no raw
SQL or SQLAlchemy imports.
"""

from __future__ import annotations

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

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

logger = logging.getLogger(__name__)


class ProxyRepository(BaseRepository[Proxy]):
    """CRUD for ``proxies`` via BaseRepository backends.

    Usage::

        repo = ProxyRepository(model=Proxy)
        async with repo:
            proxy = await repo.create_proxy({...})
            results = await repo.list_all(20, 0)
    """

    TABLE = "proxies"

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

    @hook("after_create")
    async def _log_create(self, proxy: Proxy) -> None:
        logger.debug("Proxy created: id=%s", proxy.id)

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

    @hook("after_delete")
    async def _log_delete(self, proxy: Proxy) -> None:
        logger.debug("Proxy deleted: id=%s", proxy.id)

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

    async def create_proxy(self, data: dict[str, Any]) -> Proxy:
        """Insert a new proxy row and return the domain-model instance."""
        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, proxy_id: str) -> Proxy | None:
        return await self.find_by_id(self.TABLE, proxy_id)

    async def list_all(
        self, limit: int, offset: int
    ) -> tuple[list[Proxy], int]:
        total = await self.count(self.TABLE)
        rows = await self.all(
            self.TABLE,
            limit=limit,
            offset=offset,
            order_by=["-created_at"],
        )
        return rows, total

    async def find_by_country(
        self, country_code: str, limit: int = 20, offset: int = 0
    ) -> list[Proxy]:
        return await self.find(
            self.TABLE,
            {"country_code": country_code, "valid": True},
            limit=limit,
            offset=offset,
        )

    async def find_valid(self, limit: int = 20, offset: int = 0) -> list[Proxy]:
        return await self.find(
            self.TABLE,
            {"valid": True},
            limit=limit,
            offset=offset,
            order_by=["-last_verification"],
        )

    async def delete_proxy(self, proxy_id: str) -> int:
        await self.emit("before_delete", proxy_id)
        result = await self.remove_by_id(self.TABLE, proxy_id)
        await self.emit("after_delete", proxy_id)
        return result

    async def mark_invalid(self, proxy_id: str) -> Proxy | None:
        await self.emit("before_update", proxy_id)
        result = await self.update_by_id(self.TABLE, proxy_id, {"valid": False})
        await self.emit("after_update", result)
        return result

    async def update_performance(
        self, proxy_id: str, average_delay: float, average_speed: float
    ) -> Proxy | None:
        return await self.update_by_id(
            self.TABLE,
            proxy_id,
            {
                "average_delay": average_delay,
                "average_speed": average_speed,
                "last_time_tested": datetime.now(timezone.utc),
            },
        )

    async def save_proxy(self, data: dict[str, Any]) -> Proxy:
        """Insert-or-update a proxy by ID (upsert).

        Delegates to :meth:`BaseRepository.save` which uses the ``id``
        field to decide between INSERT and UPDATE.
        """
        await self.emit("before_update", data)
        result = await self.save(self.TABLE, data, returning=["*"])
        await self.emit("after_update", result)
        return result

    async def bulk_save_proxies(self, data: list[dict[str, Any]]) -> list[Proxy]:
        """Batch insert-or-update proxies.

        Each dict in *data* is upserted individually via :meth:`save_proxy`.
        For large batches, consider wrapping in a transaction at the caller
        level if the backend supports it.
        """
        results: list[Proxy] = []
        for row in data:
            result = await self.save_proxy(row)
            results.append(result)
        return results
