import pytest
import yaml
from soundevent import data

from batdetect2.targets import terms
from batdetect2.targets.terms import (
    TagInfo,
    TermRegistry,
    load_terms_from_config,
)


def test_term_registry_initialization():
    registry = TermRegistry()
    assert registry._terms == {}

    initial_terms = {
        "test_term": data.Term(name="test", label="Test", definition="test")
    }
    registry = TermRegistry(terms=initial_terms)
    assert registry._terms == initial_terms


def test_term_registry_add_term():
    registry = TermRegistry()
    term = data.Term(name="test", label="Test", definition="test")
    registry.add_term("test_key", term)
    assert registry._terms["test_key"] == term


def test_term_registry_get_term():
    registry = TermRegistry()
    term = data.Term(name="test", label="Test", definition="test")
    registry.add_term("test_key", term)
    retrieved_term = registry.get_term("test_key")
    assert retrieved_term == term


def test_term_registry_add_custom_term():
    registry = TermRegistry()
    term = registry.add_custom_term(
        "custom_key", name="custom", label="Custom", definition="A custom term"
    )
    assert registry._terms["custom_key"] == term
    assert term.name == "custom"
    assert term.label == "Custom"
    assert term.definition == "A custom term"


def test_term_registry_add_duplicate_term():
    registry = TermRegistry()
    term = data.Term(name="test", label="Test", definition="test")
    registry.add_term("test_key", term)
    with pytest.raises(KeyError):
        registry.add_term("test_key", term)


def test_term_registry_get_term_not_found():
    registry = TermRegistry()
    with pytest.raises(KeyError):
        registry.get_term("non_existent_key")


def test_term_registry_get_keys():
    registry = TermRegistry()
    term1 = data.Term(name="test1", label="Test1", definition="test")
    term2 = data.Term(name="test2", label="Test2", definition="test")
    registry.add_term("key1", term1)
    registry.add_term("key2", term2)
    keys = registry.get_keys()
    assert set(keys) == {"key1", "key2"}


def test_get_term_from_key():
    term = terms.get_term_from_key("event")
    assert term == terms.call_type

    custom_registry = TermRegistry()
    custom_term = data.Term(name="custom", label="Custom", definition="test")
    custom_registry.add_term("custom_key", custom_term)
    term = terms.get_term_from_key("custom_key", term_registry=custom_registry)
    assert term == custom_term


def test_get_term_keys():
    keys = terms.get_term_keys()
    assert "event" in keys
    assert "individual" in keys
    assert terms.GENERIC_CLASS_KEY in keys

    custom_registry = TermRegistry()
    custom_term = data.Term(name="custom", label="Custom", definition="test")
    custom_registry.add_term("custom_key", custom_term)
    keys = terms.get_term_keys(term_registry=custom_registry)
    assert "custom_key" in keys


def test_tag_info_and_get_tag_from_info():
    tag_info = TagInfo(value="Myotis myotis", key="event")
    tag = terms.get_tag_from_info(tag_info)
    assert tag.value == "Myotis myotis"
    assert tag.term == terms.call_type


def test_get_tag_from_info_key_not_found():
    tag_info = TagInfo(value="test", key="non_existent_key")
    with pytest.raises(KeyError):
        terms.get_tag_from_info(tag_info)


def test_load_terms_from_config(tmp_path):
    term_registry = TermRegistry()
    config_data = {
        "terms": [
            {
                "key": "species",
                "name": "dwc:scientificName",
                "label": "Scientific Name",
            },
            {
                "key": "my_custom_term",
                "name": "soundevent:custom_term",
                "definition": "Describes a specific project attribute",
            },
        ]
    }
    config_file = tmp_path / "config.yaml"
    with open(config_file, "w") as f:
        yaml.dump(config_data, f)

    loaded_terms = load_terms_from_config(
        config_file,
        term_registry=term_registry,
    )
    assert "species" in loaded_terms
    assert "my_custom_term" in loaded_terms
    assert loaded_terms["species"].name == "dwc:scientificName"
    assert loaded_terms["my_custom_term"].name == "soundevent:custom_term"


def test_load_terms_from_config_file_not_found():
    with pytest.raises(FileNotFoundError):
        load_terms_from_config("non_existent_file.yaml")


def test_load_terms_from_config_validation_error(tmp_path):
    config_data = {
        "terms": [
            {
                "key": "species",
                "uri": "dwc:scientificName",
                "label": 123,
            },  # Invalid label type
        ]
    }
    config_file = tmp_path / "config.yaml"
    with open(config_file, "w") as f:
        yaml.dump(config_data, f)

    with pytest.raises(ValueError):
        load_terms_from_config(config_file)


def test_load_terms_from_config_key_already_exists(tmp_path):
    config_data = {
        "terms": [
            {
                "key": "event",
                "uri": "dwc:scientificName",
                "label": "Scientific Name",
            },  # Duplicate key
        ]
    }
    config_file = tmp_path / "config.yaml"
    with open(config_file, "w") as f:
        yaml.dump(config_data, f)

    with pytest.raises(KeyError):
        load_terms_from_config(config_file)
