from __future__ import annotations

import contextlib
import io
import json
import os
import unittest
from typing import Any, Dict
from unittest.mock import patch

from src.cli import main


def _run_preview(place: str, venue: str | None = None, event: str | None = None, env: Dict[str, str] | None = None) -> dict[str, Any]:
    args = ["naming", "preview", "--place", place]
    if venue is not None:
        args += ["--venue", venue]
    if event is not None:
        args += ["--event", event]
    stdout = io.StringIO()
    with patch.dict(os.environ, env or {}, clear=True), contextlib.redirect_stdout(stdout):
        code = main(args)
    assert code == 0
    return json.loads(stdout.getvalue().strip())


class TestNaming(unittest.TestCase):
    def test_preview_slugifies_unicode(self) -> None:
        out = _run_preview(place="Amsterdam", venue="Venue X", event="Nieuwjaarsf\u00e9\u00e9st")
        self.assertEqual(out["filename"], "amsterdam-venue-x-nieuwjaarsfeest")

    def test_preview_dedupes_tokens(self) -> None:
        out = _run_preview(place="Amsterdam", venue="Amsterdam Venue", event="Amsterdam")
        self.assertEqual(out["filename"], "amsterdam-venue")

    def test_preview_drops_denylisted_venue(self) -> None:
        out = _run_preview(place="Amsterdam", venue="Basisschool De Horizon", event="Sportdag")
        self.assertEqual(out["filename"], "amsterdam-sportdag")
        self.assertEqual(out["venue_dropped_reason"], "denylist")

    def test_preview_filters_address_like_tokens(self) -> None:
        out = _run_preview(place="Amsterdam", venue="Keizersgracht 123", event="Nieuwjaarsfeest")
        self.assertEqual(out["filename"], "amsterdam-nieuwjaarsfeest")
        self.assertEqual(out["venue_dropped_reason"], "address_only")

    def test_preview_enforces_slug_max_len(self) -> None:
        out = _run_preview(place="Amsterdam", venue="Venue X", event="Nieuwjaarsfeest", env={"SLUG_MAX_LEN": "20"})
        self.assertEqual(out["filename"], "amsterdam-venue-x")
        self.assertLessEqual(len(out["filename"]), 20)

