"""The guide document's contract: what it refuses, and what it must never be able to say.

Pure logic, no database and no network. The refusals are the point: a profile that has grown a
section, or an import that tries to write a fact that belongs somewhere else, has to stop and be
read by a person rather than be dropped or silently accepted.
"""

import pytest
from pydantic import ValidationError

from app.models.place_guide import SECTION_LABEL, PlaceGuide, SectionKind
from app.services.places import KINDS


class TestTheDocumentRefuses:
    def test_an_unknown_section_kind_fails_the_import(self):
        """A profile that grows an eighth section must reach a person. Dropping it would lose
        what the author wrote; rendering it unlabelled would put a heading-less list on the page."""
        with pytest.raises(ValidationError):
            PlaceGuide.model_validate({"sections": [{"kind": "opening_hours", "items": ["05:00"]}]})

    def test_an_unknown_field_fails_the_import(self):
        with pytest.raises(ValidationError):
            PlaceGuide.model_validate({"terminals": [{"name": "Terminal 2"}]})

    def test_the_guide_cannot_carry_hours(self):
        """Hours are observations with a provenance and live in `airport_hours`, hand over
        collected. A guide field would let a document import overwrite a person's entry."""
        for payload in ({"hours": "05:00 to 22:00"}, {"hours_provenance": {"kind": "hand"}}):
            with pytest.raises(ValidationError):
                PlaceGuide.model_validate(payload)

    def test_an_area_needs_a_code_and_a_name(self):
        with pytest.raises(ValidationError):
            PlaceGuide.model_validate({"areas": [{"name": "Terminal 2"}]})


class TestTheDocumentAccepts:
    def test_an_empty_guide_is_valid(self):
        """Every field is optional: a half-written guide is worth showing, and what is missing
        draws a placeholder in the owner's view rather than filler in the shopper's."""
        assert PlaceGuide().areas == []

    def test_the_seven_kinds_round_trip_at_both_levels(self):
        doc = {
            "sections": [{"kind": k.value, "items": [f"{k.value} line"]} for k in SectionKind],
            "areas": [{"code": "T2", "name": "Terminal 2", "strapline": "Star Alliance",
                       "sections": [{"kind": k.value, "items": ["x"]} for k in SectionKind]}],
            "closing": [{"title": "Arriving and clearing customs",
                         "sections": [{"kind": "restrictions", "items": ["4 litres of spirits"]}]}],
        }
        guide = PlaceGuide.model_validate(doc)
        assert guide.model_dump(exclude_defaults=True, mode="json") == doc
        assert guide.area_codes() == ["T2"]

    def test_an_area_is_not_a_terminal(self):
        """Zurich's areas are an airside centre and a pier; a port's are berths. The word a page
        prints comes from the place's kind, so a new kind of place is a registry line."""
        guide = PlaceGuide.model_validate({"areas": [{"code": "Dock E", "name": "Dock E"}]})
        assert guide.area_codes() == ["Dock E"]
        assert KINDS["airport"].area_label == "Terminal"
        assert KINDS["port"].area_label == "Pier"
        assert KINDS["mall"].area_label == "Floor"


def test_every_kind_has_a_label_in_the_site_s_spelling():
    """The identifiers stay American (`traveler_tips`, house style); what a reader sees does not."""
    assert set(SECTION_LABEL) == set(SectionKind)
    assert SECTION_LABEL[SectionKind.TRAVELER_TIPS] == "Traveller tips"
    assert all(label and label[0].isupper() for label in SECTION_LABEL.values())
