from collections.abc import Iterator
from uuid import uuid4

import pytest
from sqlalchemy import Engine
from sqlmodel import Session

from domain.tasks.tasks import Source, Task, TaskStatus
from infrastructure.postgres.task_table import TaskRow
from repositories.tasks.task_postgres_repository import TaskPostgresRepository


@pytest.fixture
def session(db_engine: Engine) -> Iterator[Session]:
    with Session(db_engine) as s:
        yield s


def test_create_and_get_round_trip(session: Session) -> None:
    # arrange
    repo = TaskPostgresRepository(session=session)
    t = Task.create(
        task_source="manual:test", source=Source.PORTFOLIO, source_id="abc-123"
    )

    # act
    repo.create(t)
    fetched = repo.get(t.id)

    # assert
    assert fetched.id == t.id
    assert fetched.status is TaskStatus.WAITING
    assert fetched.source is Source.PORTFOLIO
    assert fetched.source_id == "abc-123"


def test_save_persists_status_transition(session: Session) -> None:
    # arrange
    repo = TaskPostgresRepository(session=session)
    t = Task.create(task_source="manual:test")
    repo.create(t)

    # act
    t.start()
    repo.save(t)
    # assert
    assert repo.get(t.id).status is TaskStatus.IN_PROGRESS

    # act
    t.complete()
    repo.save(t)
    # assert
    done = repo.get(t.id)
    assert done.status is TaskStatus.COMPLETE
    assert done.job_completed is not None


def test_get_missing_raises(session: Session) -> None:
    # arrange
    repo = TaskPostgresRepository(session=session)
    # act / assert
    with pytest.raises(ValueError, match="not found"):
        repo.get(uuid4())


def test_get_normalises_legacy_capitalised_status(session: Session) -> None:
    # Existing rows written by backend code use "In Progress" (capitalised).
    # arrange
    repo = TaskPostgresRepository(session=session)
    row = TaskRow(task_source="manual:test", status="In Progress")
    session.add(row)
    session.commit()

    # act
    fetched = repo.get(row.id)
    # assert
    assert fetched.status is TaskStatus.IN_PROGRESS
