"""Batch management — list scoping, post-import summary, and the
delete guard re-keyed to invoice attachment (T-IMP-003/006/027/028)."""

import uuid
from datetime import datetime

import pytest
from sqlalchemy import select

from app.models import ImportBatch, Invoice, InvoiceLine, TimeEntry
from app.services.imports import batches, staging
from app.services.imports.commit import commit_pending
from app.services.imports.errors import ImportsError
from tests.authz.conftest import actor_for
from tests.imports.conftest import csv_of, csv_row


def committed_batch(graph, username="adi", *rows):
    actor = actor_for(graph, username)
    pending = staging.stage_upload(
        graph.session,
        actor,
        filename="weekly.csv",
        data=csv_of(*(rows or (csv_row(), csv_row(Description="b")))).encode(),
    )
    result = commit_pending(
        graph.session,
        actor,
        pending.id,
        {"operators": [], "clients": [], "projects": [], "buckets": []},
    )
    return uuid.UUID(result["batch_id"])


def attach_one_entry_to_invoice(graph, batch_id) -> int:
    invoice = Invoice(tenant_id=graph.tenant.id, name=f"INV-{batch_id.hex[:6]}", currency="CAD")
    graph.session.add(invoice)
    graph.session.flush()
    line = InvoiceLine(invoice_id=invoice.id, kind="time")
    graph.session.add(line)
    graph.session.flush()
    entry = graph.session.scalars(
        select(TimeEntry).where(TimeEntry.import_id == batch_id)
    ).first()
    entry.invoice_line_id = line.id
    graph.session.commit()
    return 1


def test_batch_list_counts_and_metadata(graph):
    batch_id = committed_batch(graph)
    rian = actor_for(graph, "rian")
    rows = batches.list_batches(graph.session, rian)
    row = next(r for r in rows if r["id"] == str(batch_id))
    assert row["name"] == "Jun 2, 2026"
    assert row["filename"] == "weekly.csv"
    assert row["imported_by"] == "adi"
    assert row["row_count"] == 2
    assert row["total_entries"] == 2
    assert row["applied_entries"] == 0
    assert row["locked"] is False


def test_batch_summary_lists_unknown_emails(graph):
    batch_id = committed_batch(
        graph, "adi", csv_row(), csv_row(Email="who@x.com"), csv_row(Email="who@x.com")
    )
    adi = actor_for(graph, "adi")
    summary = batches.batch_summary(graph.session, adi, batch_id)
    assert summary["total"] == 3
    assert summary["matched"] == 1
    assert summary["unmatched"] == 2
    assert summary["unknown_emails"] == ["who@x.com"]


def test_imp_003_delete_refused_with_real_count_when_invoiced(graph):
    batch_id = committed_batch(graph)
    attach_one_entry_to_invoice(graph, batch_id)
    rian = actor_for(graph, "rian")
    with pytest.raises(ImportsError) as err:
        batches.delete_batch(graph.session, rian, batch_id)
    assert err.value.code == "BATCH_LOCKED"
    assert "1 entries in this batch have already been applied" in err.value.summary
    # nothing deleted
    assert graph.session.get(ImportBatch, batch_id) is not None
    # and the list flags it locked
    row = next(
        r for r in batches.list_batches(graph.session, rian) if r["id"] == str(batch_id)
    )
    assert row["locked"] is True and row["applied_entries"] == 1


def test_imp_028_delete_removes_entries_and_batch_no_orphans(graph):
    batch_id = committed_batch(graph)
    adi = actor_for(graph, "adi")
    result = batches.delete_batch(graph.session, adi, batch_id)
    assert result["deleted_entries"] == 2
    assert graph.session.get(ImportBatch, batch_id) is None
    orphans = graph.session.scalars(
        select(TimeEntry).where(TimeEntry.import_id == batch_id)
    ).all()
    assert orphans == []


def test_imp_006_delete_needs_manage_imports(graph):
    batch_id = committed_batch(graph)
    gary = actor_for(graph, "gary")
    with pytest.raises(ImportsError) as err:
        batches.delete_batch(graph.session, gary, batch_id)
    assert err.value.summary == "You do not have permission to delete batches."


def test_imp_027_view_as_scopes_list_and_delete(graph):
    adi_batch = committed_batch(graph, "adi")
    rian_batch = committed_batch(graph, "rian", csv_row(Description="rian's"))

    viewing = actor_for(graph, "rian", cookies={"with_view_as": "adi"})
    rows = batches.list_batches(graph.session, viewing)
    ids = {r["id"] for r in rows}
    assert str(adi_batch) in ids and str(rian_batch) not in ids

    with pytest.raises(ImportsError) as err:
        batches.delete_batch(graph.session, viewing, rian_batch)
    assert err.value.code == "OUT_OF_SCOPE"
    assert graph.session.get(ImportBatch, rian_batch) is not None


def test_manual_entries_never_ride_on_batch_counts(graph):
    batch_id = committed_batch(graph)
    graph.session.add(
        TimeEntry(
            tenant_id=graph.tenant.id,
            source="manual",
            start_at=datetime(2026, 6, 3, 9, 0, 0),
            description="manual row",
        )
    )
    graph.session.commit()
    rian = actor_for(graph, "rian")
    row = next(
        r for r in batches.list_batches(graph.session, rian) if r["id"] == str(batch_id)
    )
    assert row["total_entries"] == 2
