import os
import sys
import types
from datetime import date
from decimal import Decimal
from pathlib import Path

from fastapi import FastAPI
from fastapi.testclient import TestClient
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool


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_telegram_receipts.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 "jose" not in sys.modules:
    jose_stub = types.ModuleType("jose")
    jwt_stub = types.SimpleNamespace(
        encode=lambda *args, **kwargs: "test-token",
        decode=lambda *args, **kwargs: {},
    )
    jose_stub.JWTError = Exception
    jose_stub.jwt = jwt_stub
    sys.modules["jose"] = jose_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 make_client():
    from app.api.telegram import router
    from app.core.database import get_db
    from app.core.models import Base, SystemConfig, Tenant

    engine = create_engine(
        "sqlite://",
        connect_args={"check_same_thread": False},
        poolclass=StaticPool,
    )
    SessionLocal = sessionmaker(bind=engine)
    Base.metadata.create_all(engine)
    _seed_db = SessionLocal()
    _seed_db.add_all([
        Tenant(id=1, company_code="test", name="Test"),
        SystemConfig(tenant_id=1, key_name="telegram_chat_id", key_value="12345"),
        SystemConfig(tenant_id=1, key_name="telegram_webhook_secret", key_value="test-secret", is_secret=True),
    ])
    _seed_db.commit()
    _seed_db.close()

    def override_get_db():
        db = SessionLocal()
        try:
            yield db
        finally:
            db.close()

    app = FastAPI()
    app.include_router(router)
    app.dependency_overrides[get_db] = override_get_db
    return TestClient(app), SessionLocal, engine


def test_telegram_webhook_photo_creates_only_draft_expense(monkeypatch):
    from app.api import telegram as telegram_api
    from app.core.models import Expense, ReceiptAsset

    client, SessionLocal, engine = make_client()
    sent_messages = []

    monkeypatch.setattr(
        telegram_api,
        "get_telegram_config",
        lambda: {"bot_token": "token", "chat_id": "12345"},
    )

    async def fake_download(file_id, **kwargs):
        return {
            "success": True,
            "filename": f"{file_id}.jpg",
            "content_type": "image/jpeg",
            "file_bytes": b"telegram-image",
        }

    async def fake_process_receipt_upload(db, filename, content_type, file_bytes, upload_user, source_channel, tenant_id):
        receipt = ReceiptAsset(
            tenant_id=1,
            original_filename=filename,
            storage_path_original="D:/erp/tmp/original.jpg",
            storage_path_preview="D:/erp/tmp/preview.webp",
            mime_type=content_type,
            file_size_original=len(file_bytes),
            file_size_preview=1,
            upload_user=upload_user,
            processing_status="ai_complete",
        )
        db.add(receipt)
        db.flush()
        expense = Expense(
            tenant_id=1,
            receipt_asset_id=receipt.id,
            expense_date=date(2026, 6, 8),
            vendor_name="Telegram Vendor",
            category="office",
            amount_gross=Decimal("23.00"),
            gst_amount=Decimal("3.00"),
            amount_net=Decimal("20.00"),
            currency="NZD",
            source="ai_receipt",
            status="draft",
        )
        db.add(expense)
        db.commit()
        return {
            "receipt_id": receipt.id,
            "expense_id": expense.id,
            "processing_status": receipt.processing_status,
        }

    async def fake_send(chat_id, text, **kwargs):
        sent_messages.append((chat_id, text))
        return {"success": True, "error": None, "status_code": 200}

    monkeypatch.setattr(telegram_api, "download_telegram_file", fake_download)
    monkeypatch.setattr(telegram_api.receipt_service, "process_receipt_upload", fake_process_receipt_upload)
    monkeypatch.setattr(telegram_api, "send_telegram_message", fake_send)

    response = client.post(
        "/api/telegram/webhook",
        headers={"X-Telegram-Bot-Api-Secret-Token": "test-secret"},
        json={
            "message": {
                "message_id": 9,
                "chat": {"id": 12345},
                "from": {"username": "owner"},
                "photo": [
                    {"file_id": "small", "file_size": 100},
                    {"file_id": "large", "file_size": 500},
                ],
            }
        },
    )

    assert response.status_code == 200, response.text
    assert response.json()["expense_id"] == 1

    db = SessionLocal()
    try:
        expense = db.get(Expense, 1)
        assert expense.status == "draft"
        assert expense.source == "ai_receipt"
    finally:
        db.close()
        engine.dispose()

    assert sent_messages
    assert sent_messages[0][0] == "12345"
    assert "saving" in sent_messages[0][1]
    assert "Receipt draft" in sent_messages[1][1]


def test_telegram_webhook_rejects_unauthorized_chat_without_processing(monkeypatch):
    from app.api import telegram as telegram_api

    client, _SessionLocal, engine = make_client()
    processed = []

    monkeypatch.setattr(
        telegram_api,
        "get_telegram_config",
        lambda: {"bot_token": "token", "chat_id": "12345"},
    )

    async def fake_download(file_id):
        raise AssertionError("unauthorized messages must not download files")

    async def fake_process_receipt_upload(*args, **kwargs):
        processed.append(True)
        raise AssertionError("unauthorized messages must not process receipts")

    monkeypatch.setattr(telegram_api, "download_telegram_file", fake_download)
    monkeypatch.setattr(telegram_api.receipt_service, "process_receipt_upload", fake_process_receipt_upload)

    response = client.post(
        "/api/telegram/webhook",
        headers={"X-Telegram-Bot-Api-Secret-Token": "test-secret"},
        json={
            "message": {
                "message_id": 10,
                "chat": {"id": 999},
                "photo": [{"file_id": "large", "file_size": 500}],
            }
        },
    )

    engine.dispose()

    assert response.status_code == 403, response.text
    assert processed == []
