"""Constructors for the custom loader."""

from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import yaml

from .objects import NodeDictClass, NodeStrClass
from .reference import _add_reference_to_node_class
from .reference_object import _add_reference

if TYPE_CHECKING:
    from .loader import JSON_TYPE, LoaderType

_LOGGER = logging.getLogger(__name__)


def _handle_mapping_tag(
    loader: LoaderType, node: yaml.nodes.MappingNode
) -> NodeDictClass:
    """Load YAML mappings into an ordered dictionary to preserve key order."""
    loader.flatten_mapping(node)
    nodes = loader.construct_pairs(node)

    # Check first if length of dict is equal to the length of the nodes
    # This is a quick way to check if the keys are unique
    try:
        conv_dict = NodeDictClass(nodes)
    except TypeError:
        pass
    else:
        if len(conv_dict) == len(nodes):
            _add_reference_to_node_class(conv_dict, loader, node)
            return conv_dict

    seen: dict = {}
    for (key, _), (child_node, _) in zip(nodes, node.value, strict=False):
        line = child_node.start_mark.line

        try:
            hash(key)
        except TypeError as exc:
            fname = loader.get_stream_name
            raise yaml.MarkedYAMLError(
                context=f'invalid key: "{key}"',
                context_mark=yaml.Mark(
                    fname,
                    0,
                    line,
                    -1,
                    None,
                    None,  # type: ignore[arg-type]
                ),
            ) from exc

        if key in seen:
            fname = loader.get_stream_name
            _LOGGER.warning(
                'YAML file %s contains duplicate key "%s". Check lines %d and %d',
                fname,
                key,
                seen[key],
                line,
            )
        seen[key] = line

    mapping = NodeDictClass(nodes)
    _add_reference_to_node_class(mapping, loader, node)
    return mapping


def _construct_seq(loader: LoaderType, node: yaml.nodes.Node) -> JSON_TYPE:
    """Add line number and file name to Load YAML sequence."""
    (obj,) = loader.construct_yaml_seq(node)
    return _add_reference(obj, loader, node)


def _handle_scalar_tag(
    loader: LoaderType, node: yaml.nodes.ScalarNode
) -> str | int | float | None:
    """Add line number and file name to Load YAML sequence."""
    obj = node.value
    if not isinstance(obj, str):
        return obj
    str_class = NodeStrClass(obj)
    _add_reference_to_node_class(str_class, loader, node)
    return str_class
