from typing import Optional

from semantic_models.schemas import SemanticModelSpec

from semantic_models.glossary_types import GlossaryBinding, GlossaryTerm

def normalize_term(term: str) -> str:
    return term.strip().lower()


def validate_binding(
    binding: GlossaryBinding,
    spec: SemanticModelSpec,
) -> tuple[bool, Optional[str]]:
    # Find the entity
    entity = next((e for e in spec.entities if e.name == binding.targetEntityClass), None)
    if not entity:
        return False, f"Entity not found: {binding.targetEntityClass}"

    target_type = binding.targetType
    target_id = binding.targetName

    # Entity-level binding (no target_id needed)
    if target_type == "ENTITY_CLASS":
        return True, None

    # For attribute/metric/filter, target_id is required
    if not target_id:
        return False, f"{target_type} binding requires target_id"

    if target_type == "ATTRIBUTE":
        attr = next((a for a in entity.attributes if a.name == target_id), None)
        if not attr:
            return False, f"Attribute not found: {target_id}"
        return True, None

    elif target_type == "METRIC":
        metric = next((m for m in entity.metrics if m.name == target_id), None)
        if not metric:
            return False, f"Metric not found: {target_id}"
        return True, None

    elif target_type == "FILTER":
        filt = next((f for f in entity.filters if f.name == target_id), None)
        if not filt:
            return False, f"Filter not found: {target_id}"
        return True, None

    return False, f"Unknown target_type: {target_type}"


def resolve_binding_names(
    binding: GlossaryBinding,
    spec: SemanticModelSpec,
) -> tuple[str, Optional[str]]:
    entity = next((e for e in spec.entities if e.name == binding.targetEntityClass), None)
    if not entity:
        return "Unknown", None

    entity_name = entity.name
    target_name = None

    if binding.targetType == "ENTITY_CLASS":
        return entity_name, None

    target_id = binding.targetName
    if not target_id:
        return entity_name, None

    if binding.targetType == "ATTRIBUTE":
        attr = next((a for a in entity.attributes if a.name == target_id), None)
        target_name = attr.name if attr else "Unknown"

    elif binding.targetType == "METRIC":
        metric = next((m for m in entity.metrics if m.name == target_id), None)
        target_name = metric.name if metric else "Unknown"

    elif binding.targetType == "FILTER":
        filt = next((f for f in entity.filters if f.name == target_id), None)
        target_name = filt.name if filt else "Unknown"

    return entity_name, target_name


def load_glossary_terms_for_prompt(
    semantic_model_id: str,
    spec: SemanticModelSpec,
    version_id: str,
    limit: int = 50,
) -> list[dict]:
    from semantic_models.services.factory import get_semantic_models_service

    service = get_semantic_models_service()
    version = service.get_version(semantic_model_id, version_id)

    terms_by_id = {t.id: t for t in version.glossaryTerms}
    bindings = version.glossaryBindings

    result: list[dict] = []
    for binding in bindings:
        is_valid, _ = validate_binding(binding, spec)
        if not is_valid:
            continue

        term = terms_by_id.get(binding.termId)
        if not term:
            continue

        entity_name, target_name = resolve_binding_names(binding, spec)

        target_type = binding.targetType
        if target_type == "ENTITY_CLASS":
            target_type_out = "entity"
        else:
            target_type_out = str(target_type).lower()

        result.append(
            {
                "term_id": term.id,
                "term": term.term,
                "synonyms": term.synonyms or [],
                "description": term.description,
                "entity_id": binding.targetEntityClass,
                "entity_name": entity_name,
                "target_type": target_type_out,
                "target_id": binding.targetName,
                "target_name": target_name,
            }
        )

        if limit > 0 and len(result) >= limit:
            break

    return result


def load_vocabulary_terms_for_prompt(
    semantic_model_id: str,
    version_id: str,
    limit: int = 10,
) -> list[dict]:
    if limit <= 0:
        return []

    from semantic_models.services.factory import get_semantic_models_service

    service = get_semantic_models_service()
    version = service.get_version(semantic_model_id, version_id)
    vocabulary_ids = set(version.vocabularyTermIds)

    terms = version.glossaryTerms

    result: list[dict] = []
    for term in terms:
        if term.id not in vocabulary_ids:
            continue

        result.append(
            {
                "term": term.term,
                "description": term.description,
                "synonyms": term.synonyms or [],
            }
        )

        if len(result) >= limit:
            break

    return result
