from datetime import datetime, timezone

from sqlalchemy import select, text

from scribe.models import async_session
from scribe.models.note_version import NoteVersion

# Maximum snapshots retained per note.
MAX_VERSIONS = 50

# Minimum seconds between snapshots. Prevents autosave from consuming all
# slots — with autosave at 60 s intervals, a 5-minute gate means at most
# one snapshot per editing session segment rather than one per save tick.
MIN_VERSION_INTERVAL_SECONDS = 300


async def create_version(
    user_id: int, note_id: int, body: str, title: str, tags: list[str] | None = None
) -> NoteVersion | None:
    async with async_session() as session:
        # Fetch the most recent snapshot once for both checks.
        recent = await session.execute(
            select(NoteVersion)
            .where(NoteVersion.note_id == note_id, NoteVersion.user_id == user_id)
            .order_by(NoteVersion.created_at.desc())
            .limit(1)
        )
        last = recent.scalars().first()
        if last is not None:
            # Skip if content is identical — no change, no snapshot (git semantics).
            if (
                last.body == body
                and last.title == title
                and sorted(last.tags or []) == sorted(tags or [])
            ):
                return None
            # Skip if within the minimum interval to prevent rapid-fire autosave snapshots.
            last_at = last.created_at
            if last_at.tzinfo is None:
                last_at = last_at.replace(tzinfo=timezone.utc)
            age = (datetime.now(timezone.utc) - last_at).total_seconds()
            if age < MIN_VERSION_INTERVAL_SECONDS:
                return None

        version = NoteVersion(note_id=note_id, user_id=user_id, body=body, title=title, tags=tags or [])
        session.add(version)
        await session.commit()
        await session.refresh(version)

        # Prune rolling versions beyond MAX_VERSIONS. Pinned rows
        # (pin_kind IS NOT NULL) are excluded from both the counted
        # bucket and the deletion candidate set, so they survive
        # indefinitely regardless of rolling autosave volume.
        await session.execute(
            text("""
                DELETE FROM note_versions
                WHERE id IN (
                    SELECT id FROM note_versions
                    WHERE note_id = :note_id
                      AND user_id = :user_id
                      AND pin_kind IS NULL
                    ORDER BY created_at DESC
                    OFFSET :max_versions
                )
            """).bindparams(note_id=note_id, user_id=user_id, max_versions=MAX_VERSIONS)
        )
        await session.commit()
        return version


async def list_versions(user_id: int, note_id: int) -> list[NoteVersion]:
    async with async_session() as session:
        result = await session.execute(
            select(NoteVersion)
            .where(NoteVersion.note_id == note_id, NoteVersion.user_id == user_id)
            .order_by(NoteVersion.created_at.desc())
        )
        return list(result.scalars().all())


async def get_version(user_id: int, note_id: int, version_id: int) -> NoteVersion | None:
    async with async_session() as session:
        result = await session.execute(
            select(NoteVersion).where(
                NoteVersion.id == version_id,
                NoteVersion.note_id == note_id,
                NoteVersion.user_id == user_id,
            )
        )
        return result.scalars().first()
