"""Тесты кодогенератора."""

from __future__ import annotations

import ast

import pytest

from amocrm.codegen import (
    _safe_prop_name,
    _to_snake_case,
    generate_custom_models,
)
from amocrm.models.custom_fields import CustomFieldDefinition


def _field(id: int, name: str, type: str) -> CustomFieldDefinition:
    return CustomFieldDefinition(id=id, name=name, type=type)


# --- _to_snake_case ---


@pytest.mark.parametrize(
    "input_name,expected",
    [
        ("Источник", "istochnik"),
        ("Budget Category", "budget_category"),
        ("my-field", "my_field"),
        ("  spaces  ", "spaces"),
        ("a/b\\c", "a_b_c"),
        ("123field", "123field"),
        ("", "field"),
        ("___", "field"),
        ("CamelCase", "camelcase"),
        ("пользовательское_соглашение", "polzovatelskoe_soglashenie"),
        ("дкт_client_cid", "dkt_client_cid"),
        ("предыдущая_сделка_1", "predydushchaya_sdelka_1"),
        ("Источник трафика", "istochnik_trafika"),
    ],
)
def test_to_snake_case(input_name: str, expected: str) -> None:
    assert _to_snake_case(input_name) == expected


# --- _safe_prop_name ---


def test_safe_prop_name_no_conflict():
    used: set[str] = set()
    name = _safe_prop_name("source", "leads", used)
    assert name == "source"
    assert "source" in used


def test_safe_prop_name_conflicts_with_dataclass_field():
    used: set[str] = set()
    # "name" — поле Lead
    result = _safe_prop_name("name", "leads", used)
    assert result == "name_cf"


def test_safe_prop_name_keyword_conflict():
    used: set[str] = set()
    result = _safe_prop_name("class", "leads", used)
    assert result == "class_cf"


def test_safe_prop_name_already_used():
    used: set[str] = {"source"}
    result = _safe_prop_name("source", "leads", used)
    assert result == "source_cf"


def test_safe_prop_name_multiple_conflicts():
    used: set[str] = {"source", "source_cf"}
    result = _safe_prop_name("source", "leads", used)
    assert result == "source_cf_cf"


# --- generate_custom_models ---


def _make_fields_dict(
    **extra: list[CustomFieldDefinition],
) -> dict[str, list[CustomFieldDefinition]]:
    base: dict[str, list[CustomFieldDefinition]] = {
        "leads": [
            _field(101, "Источник", "text"),
            _field(102, "Бюджет", "numeric"),
        ],
        "contacts": [
            _field(201, "Телефон", "phone"),
        ],
    }
    base.update(extra)
    return base


def test_generate_valid_python():
    code = generate_custom_models(_make_fields_dict())
    tree = ast.parse(code)  # Если невалидный Python — кинет SyntaxError
    assert tree is not None


def test_generate_contains_class_names():
    code = generate_custom_models(_make_fields_dict())
    assert "class MyLead(Lead):" in code
    assert "class MyContact(Contact):" in code


def test_generate_contains_imports():
    code = generate_custom_models(_make_fields_dict())
    assert "from amocrm import Lead, Contact" in code


def test_generate_custom_base_module():
    code = generate_custom_models(_make_fields_dict(), base_module="myapp.crm")
    assert "from myapp.crm import" in code


def test_generate_text_field():
    code = generate_custom_models({"leads": [_field(1, "Источник", "text")]})
    assert "def istochnik(self) -> str | None:" in code
    assert "get_cf_value(1)" in code


def test_generate_numeric_field():
    code = generate_custom_models({"leads": [_field(1, "Бюджет", "numeric")]})
    assert "-> float | None:" in code


def test_generate_multiselect_field():
    code = generate_custom_models({"leads": [_field(1, "Теги", "multiselect")]})
    assert "-> list[str]:" in code
    assert "get_cf_values(1)" in code


def test_generate_checkbox_field():
    code = generate_custom_models({"leads": [_field(1, "Приоритет", "checkbox")]})
    assert "-> bool | None:" in code
    assert "bool(v) if v is not None else None" in code


def test_generate_smart_address_field():
    code = generate_custom_models({"leads": [_field(1, "Адрес", "smart_address")]})
    assert "dict[str, Any] | None" in code
    assert "get_cf_raw(1)" in code
    assert "from typing import Any" in code


def test_generate_unknown_type():
    code = generate_custom_models({"leads": [_field(1, "Неизвестное", "unknown_type")]})
    tree = ast.parse(code)
    assert tree is not None
    assert "get_cf_value(1)" in code


def test_generate_name_conflict_with_dataclass_field():
    code = generate_custom_models({"leads": [_field(1, "name", "text")]})
    assert "def name_cf(self) -> str | None:" in code


def test_generate_no_fields():
    code = generate_custom_models({"leads": []})
    assert "pass" in code


def test_generate_header_comment():
    code = generate_custom_models(_make_fields_dict())
    assert "# Generated by amocrm-sdk codegen. DO NOT EDIT." in code
    assert "from __future__ import annotations" in code


def test_generate_unknown_entity_skipped():
    code = generate_custom_models({"unknown_entity": [_field(1, "field", "text")]})
    assert "class My" not in code


def test_generate_field_docstring():
    code = generate_custom_models({"leads": [_field(123, "Источник", "text")]})
    assert "Источник (text, id=123)" in code
