import asyncio
import os
import sys
import types
from decimal import Decimal
from pathlib import Path

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker


ROOT = Path(__file__).resolve().parents[1]
os.environ.setdefault("JWT_SECRET", "test-secret-value-with-enough-length-12345")
os.environ.setdefault("DB_NAME", str(ROOT / "test_receipt_processing.db"))
sys.path.insert(0, str(ROOT))

if "dotenv" not in sys.modules:
    dotenv_stub = types.ModuleType("dotenv")
    dotenv_stub.load_dotenv = lambda *args, **kwargs: None
    sys.modules["dotenv"] = dotenv_stub

if "PIL" not in sys.modules:
    pil_stub = types.ModuleType("PIL")
    image_stub = types.ModuleType("PIL.Image")
    image_stub.Image = object
    image_stub.open = lambda *args, **kwargs: None
    pil_stub.Image = image_stub
    pil_stub.UnidentifiedImageError = RuntimeError
    sys.modules["PIL"] = pil_stub
    sys.modules["PIL.Image"] = image_stub


def test_process_receipt_upload_preserves_asset_and_creates_draft_expense(monkeypatch, tmp_path):
    from app.core.models import Base, Expense, ReceiptAsset, Tenant

    existing_receipt_service = sys.modules.get("app.services.receipt_service")
    if existing_receipt_service is not None and not getattr(existing_receipt_service, "__file__", None):
        del sys.modules["app.services.receipt_service"]
        services_package = sys.modules.get("app.services")
        if services_package is not None and hasattr(services_package, "receipt_service"):
            delattr(services_package, "receipt_service")

    from app.services import receipt_service

    engine = create_engine("sqlite:///:memory:")
    SessionLocal = sessionmaker(bind=engine)
    Base.metadata.create_all(engine)
    db = SessionLocal()
    db.add(Tenant(id=1, company_code="test", name="Test"))
    db.commit()

    original_path = tmp_path / "original.png"
    preview_path = tmp_path / "preview.webp"
    renamed_path = tmp_path / "renamed.webp"
    original_path.write_bytes(b"original")
    preview_path.write_bytes(b"preview")

    monkeypatch.setattr(
        receipt_service,
        "normalize_receipt_upload",
        lambda filename, content_type, file_bytes: {
            "original_filename": filename,
            "mime_type": content_type,
            "storage_path_original": str(original_path),
            "storage_path_preview": str(preview_path),
            "file_size_original": original_path.stat().st_size,
            "file_size_preview": preview_path.stat().st_size,
            "image_width": 640,
            "image_height": 480,
            "pages": [{"path": str(preview_path), "page_number": 1}],
        },
    )
    monkeypatch.setattr(
        receipt_service,
        "run_ocr_on_pages",
        lambda paths: {"pages": [{"page_number": 1, "path": paths[0], "text": "ACME\n$115"}], "combined_text": "ACME\n$115"},
    )
    monkeypatch.setattr(receipt_service, "rename_archive_pages", lambda paths, *args: [str(renamed_path)])
    monkeypatch.setattr(receipt_service, "snapshot_json", lambda value: '{"snapshot": true}')
    renamed_path.write_bytes(b"renamed")

    async def fake_extract_receipt_data(ocr_text, original_filename, page_count):
        return {
            "vendor_name": "ACME Supplies",
            "invoice_number": "INV-9",
            "invoice_date": "2026-06-08",
            "currency": "NZD",
            "amount_gross": 115,
            "gst_amount": 15,
            "amount_net": 100,
            "category_suggestion": "office",
            "notes": "Paper and pens",
            "warnings": [],
            "confidence": "high",
        }

    try:
        result = asyncio.run(
            receipt_service.process_receipt_upload(
                db=db,
                filename="receipt.png",
                content_type="image/png",
                file_bytes=b"fake image",
                upload_user="tester",
                tenant_id=1,
                extract_func=fake_extract_receipt_data,
            )
        )

        receipt = db.get(ReceiptAsset, result["receipt_id"])
        expense = db.get(Expense, result["expense_id"])

        assert receipt is not None
        assert receipt.storage_path_original == str(original_path)
        assert receipt.storage_path_preview == str(renamed_path)
        assert receipt.processing_status == "ai_complete"
        assert receipt.upload_user == "tester"
        assert expense is not None
        assert expense.receipt_asset_id == receipt.id
        assert expense.status == "draft"
        assert expense.source == "ai_receipt"
        assert expense.vendor_name == "ACME Supplies"
        assert Decimal(str(expense.amount_gross)) == Decimal("115.00")
    finally:
        db.close()
        engine.dispose()
