"""Nexus — PushSchedule & PushRetryJob Repository (Async SQLAlchemy)"""

from typing import Optional, List
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from server.domain.models import PushSchedule, PushRetryJob


class PushScheduleRepositoryImpl:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_by_id(self, id: int) -> Optional[PushSchedule]:
        result = await self.session.execute(select(PushSchedule).where(PushSchedule.id == id))
        return result.scalar_one_or_none()

    async def get_all(self) -> List[PushSchedule]:
        result = await self.session.execute(select(PushSchedule).order_by(PushSchedule.id))
        return list(result.scalars().all())

    async def get_enabled(self) -> List[PushSchedule]:
        result = await self.session.execute(
            select(PushSchedule).where(PushSchedule.enabled == True).order_by(PushSchedule.id)
        )
        return list(result.scalars().all())

    async def create(self, schedule: PushSchedule) -> PushSchedule:
        self.session.add(schedule)
        await self.session.commit()
        return schedule

    async def update(self, schedule: PushSchedule) -> PushSchedule:
        await self.session.commit()
        return schedule

    async def delete(self, id: int) -> bool:
        schedule = await self.get_by_id(id)
        if schedule:
            await self.session.delete(schedule)
            await self.session.commit()
            return True
        return False


class PushRetryJobRepositoryImpl:
    def __init__(self, session: AsyncSession):
        self.session = session

    async def get_pending(self, limit: int = 100) -> List[PushRetryJob]:
        result = await self.session.execute(
            select(PushRetryJob)
            .where(PushRetryJob.status == "pending")
            .order_by(PushRetryJob.created_at)
            .limit(limit)
        )
        return list(result.scalars().all())

    async def get_all(self, limit: int = 100) -> List[PushRetryJob]:
        result = await self.session.execute(
            select(PushRetryJob)
            .order_by(PushRetryJob.created_at.desc())
            .limit(limit)
        )
        return list(result.scalars().all())

    async def get_by_id(self, id: int) -> Optional[PushRetryJob]:
        result = await self.session.execute(select(PushRetryJob).where(PushRetryJob.id == id))
        return result.scalar_one_or_none()

    async def delete(self, id: int) -> bool:
        job = await self.get_by_id(id)
        if job:
            await self.session.delete(job)
            await self.session.commit()
            return True
        return False

    async def create(self, job: PushRetryJob) -> PushRetryJob:
        self.session.add(job)
        await self.session.commit()
        return job

    # Allowed fields for update_status — prevents arbitrary field injection via **kwargs
    RETRY_JOB_UPDATABLE_FIELDS = {"retry_count", "max_retries", "next_retry_at", "last_error", "status"}

    async def update_status(self, id: int, status: str, **kwargs) -> PushRetryJob:
        job = await self.session.get(PushRetryJob, id)
        if job:
            job.status = status
            for key, value in kwargs.items():
                if key in self.RETRY_JOB_UPDATABLE_FIELDS:
                    setattr(job, key, value)
            await self.session.commit()
        return job