from __future__ import annotations

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))

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_CARRINHOS_ID", "test-carts")
os.environ.setdefault("APPWRITE_COLLECTION_PEDIDOS_ID", "test-orders")
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_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")

from src.app.main import app
from src.core.deps import get_carrinhos_service, require_api_key, require_user
from src.modules.carrinhos.schemas import (
    CarrinhoCreateRequest,
    CarrinhoOut,
    CarrinhoUpdateRequest,
)
from src.modules.carrinhos.service import CarrinhosService


class InMemoryCarrinhosService:
    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": [CarrinhoOut(**item).model_dump() for item in items],
        }

    async def get(self, carrinho_id: str) -> CarrinhoOut:
        if carrinho_id not in self._items:
            raise AssertionError("Carrinho not found in test store")
        return CarrinhoOut(**self._items[carrinho_id])

    async def create(
        self, payload: CarrinhoCreateRequest
    ) -> CarrinhoOut:
        data = payload.data.model_dump(exclude_unset=True)
        self._counter += 1
        requested_id = payload.documentId
        identifier = (
            requested_id.strip()
            if isinstance(requested_id, str) and requested_id.strip()
            else f"cart_{self._counter}"
        )
        timestamp = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        record = {
            "id": identifier,
            "itens": data.get("itens", []),
            "codigoInterno": data.get("codigoInterno"),
            "labNome": data.get("labNome", []),
            "quantidade": data.get("quantidade", []),
            "valorTotal": data.get("valorTotal", []),
            "usuarios": data.get("usuarios"),
            "createdAt": timestamp,
            "updatedAt": timestamp,
        }
        self._items[identifier] = record
        return CarrinhoOut(**record)

    async def update(self, carrinho_id: str, payload: CarrinhoUpdateRequest) -> CarrinhoOut:
        if carrinho_id not in self._items:
            raise AssertionError("Carrinho not found in test store")
        stored = self._items[carrinho_id].copy()
        data = payload.data.model_dump(exclude_unset=True)
        stored.update({k: v for k, v in data.items() if v is not None})
        stored["updatedAt"] = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
        self._items[carrinho_id] = stored
        return CarrinhoOut(**stored)

    async def delete(self, carrinho_id: str) -> None:
        self._items.pop(carrinho_id, None)


@pytest.fixture()
def test_client() -> TestClient:
    service = InMemoryCarrinhosService()

    def override_service() -> InMemoryCarrinhosService:
        return service

    app.dependency_overrides[get_carrinhos_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

    app.dependency_overrides.clear()
    client.close()


def test_ping_route_is_available(test_client: TestClient) -> None:
    response = test_client.get("/api/v1/ping")
    assert response.status_code == 200


def test_create_carrinho_returns_201(test_client: TestClient) -> None:
    payload = {
        "documentId": "unique()",
        "data": {
            "itens": ["item_1"],
            "codigoInterno": "CART-001",
            "labNome": ["Lab A"],
            "quantidade": [1],
            "valorTotal": [19.9],
            "usuarios": "user_1",
        },
    }
    response = test_client.post("/api/v1/carrinhos", json=payload)
    assert response.status_code == 201
    body = response.json()
    assert body["codigoInterno"] == "CART-001"
    assert body["itens"] == ["item_1"]


def test_list_carrinhos_returns_created_items(test_client: TestClient) -> None:
    payload = {
        "data": {
            "itens": ["item_1"],
            "codigoInterno": "CART-001",
            "labNome": ["Lab A"],
            "quantidade": [1],
            "valorTotal": [19.9],
            "usuarios": "user_1",
        }
    }
    test_client.post("/api/v1/carrinhos", json=payload)

    response = test_client.get("/api/v1/carrinhos")
    assert response.status_code == 200
    data = response.json()
    assert data["total"] == 1
    assert len(data["items"]) == 1


def test_update_carrinho_accepts_document_structure(test_client: TestClient) -> None:
    create_payload = {
        "documentId": "cart_patch",
        "data": {
            "itens": ["item_1", "item_2"],
            "codigoInterno": "CART-123",
            "labNome": ["Lab A", "Lab B"],
            "quantidade": [1, 2],
            "valorTotal": [10.5, 20.0],
            "usuarios": "user_1",
        },
    }
    response = test_client.post("/api/v1/carrinhos", json=create_payload)
    assert response.status_code == 201

    patch_payload = {
        "data": {
            "quantidade": [2, 3],
            "valorTotal": [21.0, 30.0],
        }
    }
    response = test_client.patch(
        "/api/v1/carrinhos/cart_patch", json=patch_payload
    )
    assert response.status_code == 200
    body = response.json()
    assert body["id"] == "cart_patch"
    assert body["quantidade"] == [2, 3]
    assert body["valorTotal"] == [21.0, 30.0]


def test_map_document_handles_relation_objects() -> None:
    document = {
        "$id": "cart_rel",
        "itens": ["item_1"],
        "codigoInterno": "CART-REL",
        "labNome": ["Lab"],
        "quantidade": [1],
        "valorTotal": [10.0],
        "usuarios": {"$id": "user_rel", "name": "User"},
        "$createdAt": "2025-09-29T12:00:00.000Z",
        "$updatedAt": "2025-09-29T12:05:00.000Z",
    }

    result = CarrinhosService._map_document(document)

    assert result.usuarios == "user_rel"


def test_map_document_handles_relation_lists() -> None:
    document = {
        "$id": "cart_rel",
        "itens": ["item_1"],
        "codigoInterno": "CART-REL",
        "labNome": ["Lab"],
        "quantidade": [1],
        "valorTotal": [10.0],
        "usuarios": [
            {"$id": "user_rel", "name": "User"},
            {"$id": "user_secondary"},
        ],
        "$createdAt": "2025-09-29T12:00:00.000Z",
        "$updatedAt": "2025-09-29T12:05:00.000Z",
    }

    result = CarrinhosService._map_document(document)

    assert result.usuarios == "user_rel"


def test_sanitize_payload_accepts_relation_objects() -> None:
    service = CarrinhosService(databases=object(), database_id="db", collection_id="col")

    sanitized = service._sanitize_payload(
        {
            "codigoInterno": "  CART-REL  ",
            "usuarios": {"$id": "user_rel"},
        }
    )

    assert sanitized["codigoInterno"] == "CART-REL"
    assert sanitized["usuarios"] == "user_rel"
