"""Contract tests for dish chooser endpoints."""

from __future__ import annotations

import os
from pathlib import Path
from unittest import TestCase

from pydantic import ValidationError

_ = os.environ.setdefault("NEON_DATABASE_URL", "postgresql://user:pass@localhost:5432/testdb")

_REPO_ROOT = Path(__file__).resolve().parents[2]


class _SourceMixin:
    def _read_source(self, relative_path: str) -> str:
        return (_REPO_ROOT / relative_path).read_text(encoding="utf-8")

    def _get_block(self, source: str, marker: str) -> str:
        idx = source.index(marker)
        rest = source[idx:]
        next_positions = [
            pos
            for pos in (
                rest.find("\nasync def ", 10),
                rest.find("\ndef ", 10),
                rest.find("\nclass ", 10),
            )
            if pos > 0
        ]
        return rest[: min(next_positions)] if next_positions else rest


class TestPublicMenuEndpointContract(_SourceMixin, TestCase):
    def test_public_menu_item_read_fields_match_contract(self) -> None:
        from app.routers.public import PublicMenuItemRead

        fields = PublicMenuItemRead.model_fields

        self.assertTrue(fields["id"].is_required())
        self.assertTrue(fields["name"].is_required())
        self.assertTrue(fields["category"].is_required())
        self.assertTrue(fields["price_cents"].is_required())
        self.assertFalse(fields["description"].is_required())
        self.assertIsNone(fields["description"].default)

    def test_public_menu_endpoint_function_exists(self) -> None:
        from app.routers import public

        self.assertTrue(hasattr(public, "get_public_menu_items"))

    def test_public_menu_endpoint_filters_to_active_menu_items(self) -> None:
        source = self._read_source("backend/app/routers/public.py")
        block = self._get_block(source, "async def get_public_menu_items")

        self.assertIn("select(MenuItem)", block)
        self.assertIn("MenuItem.is_active == True", block)


class TestAdminDishSubmissionContract(_SourceMixin, TestCase):
    def test_dish_submission_request_enforces_mutual_exclusivity(self) -> None:
        from app.schemas.dish_chooser import DishItem, DishSubmissionRequest

        valid_menu = DishSubmissionRequest(dishes=[DishItem(menu_item_id="item-1", quantity=2)])
        self.assertIsNone(valid_menu.dishes_text)

        valid_text = DishSubmissionRequest(dishes_text="2x ramen")
        self.assertEqual(valid_text.dishes_text, "2x ramen")

        with self.assertRaises(ValidationError):
            DishSubmissionRequest(
                dishes=[DishItem(menu_item_id="item-1", quantity=1)],
                dishes_text="also text",
            )

        with self.assertRaises(ValidationError):
            DishSubmissionRequest()

    def test_submit_dishes_endpoint_function_exists(self) -> None:
        from app.routers import reservations

        self.assertTrue(hasattr(reservations, "submit_dishes"))

    def test_submit_dishes_endpoint_checks_feature_disabled(self) -> None:
        source = self._read_source("backend/app/routers/reservations.py")
        block = self._get_block(source, "async def submit_dishes")

        self.assertIn('if not settings.get("dish_chooser_enabled")', block)
        self.assertIn("status_code=400", block)

    def test_submit_dishes_endpoint_checks_existing_dishes_note(self) -> None:
        source = self._read_source("backend/app/routers/reservations.py")
        block = self._get_block(source, "async def submit_dishes")

        self.assertIn('"[Dishes]" in reservation.notes', block)
        self.assertIn("status_code=409", block)

    def test_submit_dishes_endpoint_uses_validation_services(self) -> None:
        source = self._read_source("backend/app/routers/reservations.py")
        block = self._get_block(source, "async def submit_dishes")

        self.assertIn("validate_menu_dishes", block)
        self.assertIn("validate_freetext_dishes", block)
        self.assertIn("if body.dishes:", block)
        self.assertIn("elif body.dishes_text:", block)

    def test_submit_dishes_endpoint_updates_notes_via_restate(self) -> None:
        source = self._read_source("backend/app/routers/reservations.py")
        block = self._get_block(source, "async def submit_dishes")

        self.assertIn("format_dishes_note", block)
        self.assertIn("await restate_proxy(", block)
        self.assertIn('"update"', block)
        self.assertIn('{"notes": new_notes, "restaurant_id": restaurant.id}', block)


class TestReservationSchemaDishFields(TestCase):
    def test_reservation_create_dish_fields_are_optional_with_none_defaults(self) -> None:
        from app.models.reservation import ReservationCreate

        fields = ReservationCreate.model_fields

        self.assertIn("dishes", fields)
        self.assertIn("dishes_text", fields)
        self.assertFalse(fields["dishes"].is_required())
        self.assertFalse(fields["dishes_text"].is_required())
        self.assertIsNone(fields["dishes"].default)
        self.assertIsNone(fields["dishes_text"].default)

    def test_public_reservation_create_dish_fields_are_optional_with_none_defaults(self) -> None:
        from app.routers.public import PublicReservationCreate

        fields = PublicReservationCreate.model_fields

        self.assertIn("dishes", fields)
        self.assertIn("dishes_text", fields)
        self.assertFalse(fields["dishes"].is_required())
        self.assertFalse(fields["dishes_text"].is_required())
        self.assertIsNone(fields["dishes"].default)
        self.assertIsNone(fields["dishes_text"].default)
