"""Compatibility factory for non-graph variable bootstrapping.

Graph runtime segment/variable conversions live under `graphon.variables`.
This module keeps the application-layer mapping helpers and re-exports the
shared conversion functions for legacy callers and tests.
"""

from collections.abc import Mapping, Sequence
from typing import Any, cast

from configs import dify_config
from core.workflow.variable_prefixes import (
    CONVERSATION_VARIABLE_NODE_ID,
    ENVIRONMENT_VARIABLE_NODE_ID,
)
from graphon.variables.exc import VariableError
from graphon.variables.factory import (
    TypeMismatchError,
    UnsupportedSegmentTypeError,
    build_segment,
    build_segment_with_type,
    segment_to_variable,
)
from graphon.variables.types import SegmentType
from graphon.variables.variables import (
    ArrayBooleanVariable,
    ArrayNumberVariable,
    ArrayObjectVariable,
    ArrayStringVariable,
    BooleanVariable,
    FloatVariable,
    IntegerVariable,
    ObjectVariable,
    SecretVariable,
    StringVariable,
    VariableBase,
)

__all__ = [
    "TypeMismatchError",
    "UnsupportedSegmentTypeError",
    "build_conversation_variable_from_mapping",
    "build_environment_variable_from_mapping",
    "build_pipeline_variable_from_mapping",
    "build_segment",
    "build_segment_with_type",
    "segment_to_variable",
]


def build_conversation_variable_from_mapping(mapping: Mapping[str, Any], /) -> VariableBase:
    if not mapping.get("name"):
        raise VariableError("missing name")
    return _build_variable_from_mapping(mapping=mapping, selector=[CONVERSATION_VARIABLE_NODE_ID, mapping["name"]])


def build_environment_variable_from_mapping(mapping: Mapping[str, Any], /) -> VariableBase:
    if not mapping.get("name"):
        raise VariableError("missing name")
    return _build_variable_from_mapping(mapping=mapping, selector=[ENVIRONMENT_VARIABLE_NODE_ID, mapping["name"]])


def build_pipeline_variable_from_mapping(mapping: Mapping[str, Any], /) -> VariableBase:
    if not mapping.get("variable"):
        raise VariableError("missing variable")
    return mapping["variable"]


def _build_variable_from_mapping(*, mapping: Mapping[str, Any], selector: Sequence[str]) -> VariableBase:
    """
    This factory function is used to create the environment variable or the conversation variable,
    not support the File type.
    """
    if (value_type := mapping.get("value_type")) is None:
        raise VariableError("missing value type")
    if (value := mapping.get("value")) is None:
        raise VariableError("missing value")

    result: VariableBase
    match value_type:
        case SegmentType.STRING:
            result = StringVariable.model_validate(mapping)
        case SegmentType.SECRET:
            result = SecretVariable.model_validate(mapping)
        case SegmentType.NUMBER | SegmentType.INTEGER if isinstance(value, int):
            mapping = dict(mapping)
            mapping["value_type"] = SegmentType.INTEGER
            result = IntegerVariable.model_validate(mapping)
        case SegmentType.NUMBER | SegmentType.FLOAT if isinstance(value, float):
            mapping = dict(mapping)
            mapping["value_type"] = SegmentType.FLOAT
            result = FloatVariable.model_validate(mapping)
        case SegmentType.BOOLEAN:
            result = BooleanVariable.model_validate(mapping)
        case SegmentType.NUMBER if not isinstance(value, float | int):
            raise VariableError(f"invalid number value {value}")
        case SegmentType.OBJECT if isinstance(value, dict):
            result = ObjectVariable.model_validate(mapping)
        case SegmentType.ARRAY_STRING if isinstance(value, list):
            result = ArrayStringVariable.model_validate(mapping)
        case SegmentType.ARRAY_NUMBER if isinstance(value, list):
            result = ArrayNumberVariable.model_validate(mapping)
        case SegmentType.ARRAY_OBJECT if isinstance(value, list):
            result = ArrayObjectVariable.model_validate(mapping)
        case SegmentType.ARRAY_BOOLEAN if isinstance(value, list):
            result = ArrayBooleanVariable.model_validate(mapping)
        case _:
            raise VariableError(f"not supported value type {value_type}")
    if result.size > dify_config.MAX_VARIABLE_SIZE:
        raise VariableError(f"variable size {result.size} exceeds limit {dify_config.MAX_VARIABLE_SIZE}")
    if not result.selector:
        result = result.model_copy(update={"selector": selector})
    return cast(VariableBase, result)
