from __future__ import annotations

import tempfile
import unittest
from datetime import date
from pathlib import Path

from arr_ingestion.repository import InMemoryIngestionRepository
from arr_ingestion.service import IngestionService
from arr_ingestion.validation import DeliveryValidator
from arr_processing.local import LocalDailyProcessor
from arr_processing.policy import load_processor_policy
from arr_storage.filesystem import FilesystemObjectBackend
from arr_storage.store import ManagedObjectStore
from arr_web.programmatic import ProgrammaticUploadCoordinator
from tests.test_arr_opera_daily_ingest import (
    reservation,
    success_xml,
    xml_document,
)


PROJECT_ROOT = Path(__file__).resolve().parents[1]


def coordinator(root: Path):
    store = ManagedObjectStore(
        FilesystemObjectBackend(root / "objects", create=True)
    )
    repository = InMemoryIngestionRepository()
    policy = load_processor_policy(PROJECT_ROOT)
    service = IngestionService(DeliveryValidator(store, policy), repository)
    return (
        ProgrammaticUploadCoordinator(
            object_store=store,
            ingestion_repository=repository,
            ingestion_service=service,
            processor=LocalDailyProcessor(policy),
            processor_version=policy.processor_version,
            rule_set_sha256=policy.rule_set_sha256,
        ),
        repository,
        root / "objects",
    )


class ProgrammaticUploadTests(unittest.TestCase):
    def test_valid_xml_reaches_atomic_terminal_success_without_remote_boundary(self):
        with tempfile.TemporaryDirectory() as temporary:
            upload, repository, objects = coordinator(Path(temporary))

            receipt = upload.submit("ARR.XML", success_xml().encode("utf-8"))

            self.assertEqual(receipt["status"], "succeeded")
            self.assertEqual(receipt["ingestion_status"], "committed")
            self.assertEqual(receipt["business_date"], "2026-07-27")
            self.assertEqual(receipt["version_no"], 1)
            self.assertEqual(
                repository._jobs[receipt["job_id"]].registration.uploaded_filename,
                "ARR.XML",
            )
            self.assertEqual(
                repository.current_version_id(date(2026, 7, 27)),
                receipt["daily_version_id"],
            )
            records = repository.version_records(int(receipt["daily_version_id"]))
            self.assertEqual(len(records), 5)
            self.assertEqual(
                sum(1 for item in records if item["outcome"] == "retained"),
                3,
            )
            committed = {
                path.name
                for path in objects.rglob("*")
                if path.is_file() and not path.name.endswith(".arr-metadata.json")
            }
            self.assertEqual(
                committed,
                {"source.xml", "result.json", "structured-result.json", "daily-report.xlsx"},
            )

    def test_business_failure_is_recorded_without_partial_active_version(self):
        price_failure = xml_document(
            reservation(1, rate_code="GRPA4", rate_amount="1800"),
            reservation(2, rate_code="NOT-ALLOWED", rate_amount="INVALID"),
        )
        with tempfile.TemporaryDirectory() as temporary:
            upload, repository, objects = coordinator(Path(temporary))

            receipt = upload.submit("ARR.XML", price_failure.encode("utf-8"))

            self.assertEqual(receipt["status"], "failed")
            self.assertEqual(receipt["ingestion_status"], "recorded_failure")
            self.assertIsNone(receipt["version_no"])
            self.assertIsNone(repository.current_version_id(date(2026, 7, 27)))
            committed = {
                path.name
                for path in objects.rglob("*")
                if path.is_file() and not path.name.endswith(".arr-metadata.json")
            }
            self.assertEqual(
                committed,
                {"source.xml", "result.json", "structured-result.json", "exception-report.xlsx"},
            )


if __name__ == "__main__":
    unittest.main()
