from collections.abc import Iterator
from uuid import UUID, uuid4

import pytest
from sqlalchemy import Engine
from sqlmodel import Session

from domain.tasks.subtasks import SubTask, SubTaskStatus
from domain.tasks.tasks import Task
from repositories.tasks.subtask_postgres_repository import SubTaskPostgresRepository
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 _persisted_task_id(session: Session) -> UUID:
    """Create a parent Task row so SubTask FK constraints are satisfied."""
    task = Task.create(task_source="manual:test")
    TaskPostgresRepository(session=session).create(task)
    return task.id


def test_create_and_get_round_trip_preserves_inputs(session: Session) -> None:
    # arrange
    repo = SubTaskPostgresRepository(session=session)
    task_id = _persisted_task_id(session)
    st = SubTask.create(task_id=task_id, inputs={"address": "68 Glendon Way"})

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

    # assert
    assert fetched.id == st.id
    assert fetched.task_id == task_id
    assert fetched.status is SubTaskStatus.WAITING
    assert fetched.inputs == {"address": "68 Glendon Way"}
    assert fetched.outputs is None


def test_save_persists_status_and_outputs(session: Session) -> None:
    # arrange
    repo = SubTaskPostgresRepository(session=session)
    st = SubTask.create(task_id=_persisted_task_id(session))
    repo.create(st)

    # act
    st.start(cloud_logs_url="https://example/log")
    repo.save(st)
    # assert
    assert repo.get(st.id).status is SubTaskStatus.IN_PROGRESS

    # act
    st.complete({"uprn": "123"})
    repo.save(st)
    # assert
    done = repo.get(st.id)
    assert done.status is SubTaskStatus.COMPLETE
    assert done.outputs == {"result": {"uprn": "123"}}
    assert done.cloud_logs_url == "https://example/log"
    assert done.job_completed is not None


def test_list_by_task_filters_by_task_id(session: Session) -> None:
    # arrange
    repo = SubTaskPostgresRepository(session=session)
    task_a = _persisted_task_id(session)
    task_b = _persisted_task_id(session)
    repo.create(SubTask.create(task_id=task_a))
    repo.create(SubTask.create(task_id=task_a))
    repo.create(SubTask.create(task_id=task_b))

    # act
    a_results = repo.list_by_task(task_a)
    b_results = repo.list_by_task(task_b)

    # assert
    assert len(a_results) == 2
    assert len(b_results) == 1
    assert all(s.task_id == task_a for s in a_results)
    assert all(s.task_id == task_b for s in b_results)


def test_list_by_task_returns_empty_for_unknown_task(session: Session) -> None:
    # arrange
    repo = SubTaskPostgresRepository(session=session)
    # act / assert
    assert repo.list_by_task(uuid4()) == []


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