"""Tests for source revision behavior in TranscriptionService."""

from uuid import uuid4

import pytest

from transcription.db.models import Document
from transcription.db.models import Job
from transcription.db.models import JobSource
from transcription.db.models import JobSourceStatus
from transcription.db.models import JobStatus
from transcription.db.models import Source
from transcription.services.documents import DocumentService
from transcription.services.jobs import JobService
from transcription.services.transcription import SourceDeleteBlockedError
from transcription.services.transcription import TranscriptionNotFoundError
from transcription.services.transcription import TranscriptionService


@pytest.mark.integration
class TestTranscriptionServiceRevisionUpsert:
    """Verify page-level source revision semantics."""

    @pytest.mark.asyncio
    async def test_upsert_revision_creates_new_revision(self, default_session_factory):
        documents = DocumentService(session_factory=default_session_factory)
        jobs = JobService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)

        document = Document(id=uuid4(), name="revision-create")
        await documents.create_document(document=document)

        job = Job(document_id=document.id, status=JobStatus.TRANSCRIBED)
        await jobs.create_job(job=job)

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="source.jpg",
            filename="source.jpg",
            file_path="uploads/source.jpg",
        )
        async with transcriptions._session_scope() as session:
            session.add(source)
            await session.flush()
            session.add(JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING))
            await session.commit()
            await session.refresh(source)

        revision = await transcriptions.upsert_revision_for_source(source_id=source.id, text="User revision")
        fetched = await transcriptions.read_revision_by_source(source.id)

        assert revision.id == source.id
        assert revision.revised_text == "User revision"
        assert fetched is not None
        assert fetched.id == source.id
        assert fetched.revised_text == "User revision"

    @pytest.mark.asyncio
    async def test_upsert_revision_updates_existing_single_revision(self, default_session_factory):
        documents = DocumentService(session_factory=default_session_factory)
        jobs = JobService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)

        document = Document(id=uuid4(), name="revision-update")
        await documents.create_document(document=document)

        job = Job(document_id=document.id, status=JobStatus.TRANSCRIBED)
        await jobs.create_job(job=job)

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="source.jpg",
            filename="source.jpg",
            file_path="uploads/source.jpg",
        )
        async with transcriptions._session_scope() as session:
            session.add(source)
            await session.flush()
            session.add(JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING))
            await session.commit()
            await session.refresh(source)

        first = await transcriptions.upsert_revision_for_source(source_id=source.id, text="Revision v1")
        second = await transcriptions.upsert_revision_for_source(source_id=source.id, text="Revision v2")
        revisions = await transcriptions.list_revisions_by_job(job.id)

        assert first.id == second.id
        assert second.revised_text == "Revision v2"
        assert len(revisions) == 1
        assert revisions[0].id == first.id
        assert revisions[0].revised_text == "Revision v2"

    @pytest.mark.asyncio
    async def test_delete_source_from_job_context_removes_source_and_single_link(self, default_session_factory, tmp_path):
        documents = DocumentService(session_factory=default_session_factory)
        jobs = JobService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)
        transcriptions.settings.upload_dir = tmp_path

        document = Document(id=uuid4(), name="delete-source-success")
        await documents.create_document(document=document)

        job = Job(document_id=document.id, status=JobStatus.QUEUED)
        await jobs.create_job(job=job)

        stored_path = tmp_path / "documents" / str(document.id) / "delete.jpg"
        stored_path.parent.mkdir(parents=True, exist_ok=True)
        stored_path.write_bytes(b"data")

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="delete.jpg",
            filename="delete.jpg",
            file_path=str(stored_path),
        )
        async with transcriptions._session_scope() as session:
            session.add(source)
            await session.flush()
            session.add(JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING))
            await session.commit()
            await session.refresh(source)

        await transcriptions.delete_source_from_job_context(job_id=job.id, source_id=source.id)

        with pytest.raises(TranscriptionNotFoundError):
            await transcriptions.read_source(source.id)
        assert not stored_path.exists()

    @pytest.mark.asyncio
    async def test_delete_source_from_job_context_blocks_when_other_job_links_exist(self, default_session_factory):
        documents = DocumentService(session_factory=default_session_factory)
        jobs = JobService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)

        document = Document(id=uuid4(), name="delete-source-blocked")
        await documents.create_document(document=document)

        job_one = Job(document_id=document.id, status=JobStatus.QUEUED)
        job_two = Job(document_id=document.id, status=JobStatus.QUEUED)
        await jobs.create_job(job=job_one)
        await jobs.create_job(job=job_two)

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="shared.jpg",
            filename="shared.jpg",
            file_path="uploads/shared.jpg",
        )
        async with transcriptions._session_scope() as session:
            session.add(source)
            await session.flush()
            session.add(JobSource(job_id=job_one.id, source_id=source.id, status=JobSourceStatus.PENDING))
            session.add(JobSource(job_id=job_two.id, source_id=source.id, status=JobSourceStatus.PENDING))
            await session.commit()
            await session.refresh(source)

        with pytest.raises(SourceDeleteBlockedError):
            await transcriptions.delete_source_from_job_context(job_id=job_one.id, source_id=source.id)

    @pytest.mark.asyncio
    async def test_delete_unlinked_source_succeeds(self, default_session_factory, tmp_path):
        documents = DocumentService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)
        transcriptions.settings.upload_dir = tmp_path

        document = Document(id=uuid4(), name="delete-unlinked-source")
        await documents.create_document(document=document)

        stored_path = tmp_path / "documents" / str(document.id) / "orphan.jpg"
        stored_path.parent.mkdir(parents=True, exist_ok=True)
        stored_path.write_bytes(b"data")

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="orphan.jpg",
            filename="orphan.jpg",
            file_path=str(stored_path),
        )
        await transcriptions.create_source(source=source)

        await transcriptions.delete_unlinked_source(source_id=source.id)

        with pytest.raises(TranscriptionNotFoundError):
            await transcriptions.read_source(source.id)
        assert not stored_path.exists()

    @pytest.mark.asyncio
    async def test_delete_unlinked_source_blocks_when_linked(self, default_session_factory):
        documents = DocumentService(session_factory=default_session_factory)
        jobs = JobService(session_factory=default_session_factory)
        transcriptions = TranscriptionService(session_factory=default_session_factory)

        document = Document(id=uuid4(), name="delete-unlinked-blocked")
        await documents.create_document(document=document)

        job = Job(document_id=document.id, status=JobStatus.QUEUED)
        await jobs.create_job(job=job)

        source = Source(
            document_id=document.id,
            page_number=1,
            upload_name="linked.jpg",
            filename="linked.jpg",
            file_path="uploads/linked.jpg",
        )
        async with transcriptions._session_scope() as session:
            session.add(source)
            await session.flush()
            session.add(JobSource(job_id=job.id, source_id=source.id, status=JobSourceStatus.PENDING))
            await session.commit()
            await session.refresh(source)

        with pytest.raises(SourceDeleteBlockedError):
            await transcriptions.delete_unlinked_source(source_id=source.id)
