from __future__ import annotations

import importlib
import os
import sys
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict

import pytest
from fastapi.testclient import TestClient

PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))

from src.modules.pagamentos.service import PagamentosService

os.environ.setdefault("APPWRITE_ENDPOINT", "http://localhost/v1")
os.environ.setdefault("APPWRITE_PROJECT_ID", "test-project")
os.environ.setdefault("APPWRITE_API_KEY", "test-key")
os.environ.setdefault("APPWRITE_DATABASE_ID", "test-db")
os.environ.setdefault("APPWRITE_COLLECTION_CATEGORIAS_ID", "test-categorias")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_CATALOGO_ID", "test-produtos")
os.environ.setdefault("APPWRITE_COLLECTION_LABORATORIOS_ID", "test-labs")
os.environ.setdefault("APPWRITE_COLLECTION_CATEGORIAS_ID", "test-cat-rel")
os.environ.setdefault("APPWRITE_COLLECTION_SUBCATEGORIAS_ID", "test-subcat")
os.environ.setdefault("APPWRITE_COLLECTION_CARRINHOS_ID", "test-carts")
os.environ.setdefault("APPWRITE_COLLECTION_PEDIDOS_ID", "test-orders")
os.environ.setdefault("APPWRITE_COLLECTION_PAGAMENTOS_ID", "test-payments")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_ESTOQUE_ID", "test-produtos-estoque")
os.environ.setdefault("APPWRITE_COLLECTION_PRODUTOS_VENDA_ID", "test-produtos-venda")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_DATA_ID", "test-usuarios-data")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_PERFIL_ID", "test-usuarios-perfil")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIO_EMPRESA_PERFIS_ID", "test-usuario-empresa-perfis")
os.environ.setdefault("APPWRITE_COLLECTION_FATURAS_ID", "test-faturas")
os.environ.setdefault("APPWRITE_COLLECTION_USUARIOS_ID", "test-users")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_ID", "test-companies")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_DADOS_ID", "test-companies-data")
os.environ.setdefault("APPWRITE_COLLECTION_EMPRESAS_SOCIOS_ID", "test-empresas-socios")
os.environ.setdefault("APPWRITE_COLLECTION_ENDERECOS_ID", "test-addresses")
os.environ.setdefault("APPWRITE_COLLECTION_ENTREGAS_ID", "test-entregas")
os.environ.setdefault("JWT_SECRET", "test-secret")
os.environ.setdefault("JWT_ALGORITHM", "HS256")


class InMemoryPagamentosService:
    def __init__(self) -> None:
        self._items: Dict[str, Dict[str, object]] = {}
        self._counter = 0

    async def list(
        self,
        page: int,
        limit: int | None,
        q: str | None,
        sort: str | None,
        order: str | None,
    ) -> Dict[str, object]:
        limit_value = limit or 20
        items = list(self._items.values())
        total = len(items)
        return {
            "page": page,
            "limit": limit_value,
            "total": total,
            "items": items,
        }

    async def get(self, pagamento_id: str):
        if pagamento_id not in self._items:
            raise AssertionError("Pagamento not found in test store")
        return self._items[pagamento_id]

    async def create(self, payload) -> Dict[str, object]:  # pragma: no cover - not used
        self._counter += 1
        identifier = f"pay_{self._counter}"
        timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        record = {
            "id": identifier,
            "status": payload.status,
            "metodo": payload.metodo,
            "valor": payload.valor,
            "pedidos": payload.pedidos,
            "createdAt": timestamp,
            "updatedAt": timestamp,
        }
        self._items[identifier] = record
        return record

    async def update(self, pagamento_id: str, payload) -> Dict[str, object]:  # pragma: no cover - not used
        if pagamento_id not in self._items:
            raise AssertionError("Pagamento not found in test store")
        stored = self._items[pagamento_id].copy()
        for key, value in payload.model_dump(exclude_unset=True).items():
            if value is not None:
                stored[key] = value
        stored["updatedAt"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        self._items[pagamento_id] = stored
        return stored

    async def delete(self, pagamento_id: str) -> None:  # pragma: no cover - not used
        self._items.pop(pagamento_id, None)


@pytest.fixture()
def pagamentos_client() -> tuple[TestClient, str, InMemoryPagamentosService]:
    from src.core import config as config_module
    from src.core.deps import get_pagamentos_service, require_api_key, require_user

    original_api_key = os.environ.get("SECURITY_API_KEY", "")
    api_key = "test-pay-key"
    os.environ["SECURITY_API_KEY"] = api_key

    config_module.get_settings.cache_clear()  # type: ignore[attr-defined]

    main_module = importlib.import_module("src.main")
    importlib.reload(main_module)
    app_module = importlib.import_module("src.app.main")
    importlib.reload(app_module)
    app = app_module.app

    service = InMemoryPagamentosService()

    def override_service() -> InMemoryPagamentosService:
        return service

    app.dependency_overrides[get_pagamentos_service] = override_service
    app.dependency_overrides[require_api_key] = lambda: None
    app.dependency_overrides[require_user] = lambda: {"id": "user_1"}

    client = TestClient(app)
    yield client, api_key, service

    client.close()
    app.dependency_overrides.clear()
    os.environ["SECURITY_API_KEY"] = original_api_key
    config_module.get_settings.cache_clear()  # type: ignore[attr-defined]
    importlib.reload(main_module)
    importlib.reload(app_module)


def test_list_pagamentos_requires_api_key(
    pagamentos_client: tuple[TestClient, str, InMemoryPagamentosService]
) -> None:
    client, api_key, _ = pagamentos_client

    response = client.get("/api/v1/pagamentos")
    assert response.status_code == 401

    response_with_key = client.get("/api/v1/pagamentos", headers={"x-api-key": api_key})
    assert response_with_key.status_code == 200


def test_get_pagamento_route_available(
    pagamentos_client: tuple[TestClient, str, InMemoryPagamentosService]
) -> None:
    client, api_key, service = pagamentos_client

    service._items["pay_test"] = {
        "id": "pay_test",
        "status": "pendente",
        "metodo": "pix",
        "valor": 10.0,
        "pedidos": "ped_test",
        "createdAt": "2025-10-01T12:00:00.000Z",
        "updatedAt": "2025-10-01T12:00:00.000Z",
    }

    response = client.get("/api/v1/pagamentos/pay_test", headers={"x-api-key": api_key})
    assert response.status_code == 200
    body = response.json()
    assert body["id"] == "pay_test"
    assert body["status"] == "pendente"


def test_pagamentos_service_map_document_handles_relation_dict() -> None:
    document = {
        "$id": "pay_123",
        "status": "pendente",
        "metodo": "pix",
        "valor": 29.9,
        "pedidos": {"$id": "ped_456", "$collectionId": "pedidos"},
        "$createdAt": "2024-01-01T00:00:00.000Z",
        "$updatedAt": "2024-01-01T00:00:00.000Z",
    }

    mapped = PagamentosService._map_document(document)

    assert mapped.pedidos == "ped_456"
