"""Functions to help validate objects and UV maps to avoid potential errors."""

from typing import Tuple

from bpy.types import Object

from rizomuvbridge_ultimate import constants


def validate_uvmap_names(obj: Object) -> Tuple[bool, str]:
    """Validate object's UV maps for RizomUV.

    -- Assert at least one UV map.
    -- Assert that UV maps follow naming rules (no rizom special sequences).

    Args:
    ----
        obj: Object to test UV maps.

    Returns:
    -------
        True if UV maps are valid, info message

    """
    uvmaps = obj.data.uv_layers

    if not uvmaps:
        return False, f"<{obj.name}> has no UV sets, assign at least one UV set."

    for uvmap in uvmaps:
        for sequence in constants.RESERVED_UVMAP_SEQUENCES.keys():
            if sequence in uvmap.name:
                return False, (
                    f"<{obj.name}> has a UV map named <{uvmap.name}> that "
                    f"contains a reserved sequence. <{sequence}> is not allowed."
                )

    return True, "UV Sets are valid"


def validate_uvmap_name(obj: Object, name: str) -> Tuple[bool, str]:
    """Validate object's UV map name for RizomUV.

    Args:
    ----
        obj: UVMap owner.
        name: UVMap name to test.

    Returns:
    -------
        True if name is valid, info message

    """
    if not name:
        return False, f"<{obj.name}> has no UV sets, assign at least one UV set."

    for sequence in constants.RESERVED_UVMAP_SEQUENCES.keys():
        if sequence in name:
            return False, (
                f"<{obj.name}> has a UV map named <{name}> that "
                f"contains a reserved sequence. <{sequence}> is not allowed."
            )

    return True, "UV Set name is valid"


def repair_uvmap_name(name: str) -> str:
    """Edit name to avoid reserved sequences.

    Args:
    ----
        name: UVNap name to edit.

    Returns:
    -------
        Edited UVMap name

    """
    for sequence in constants.RESERVED_UVMAP_SEQUENCES.keys():
        if sequence in name:
            name = name.replace(sequence, constants.RESERVED_UVMAP_SEQUENCES[sequence])

    return name


def repair_uvmaps_copy_template(template_object: Object, target_objects: list[Object]):
    """Copy UV maps from template object to target objects.

    Args:
    ----
        template_object: Object with valid UVMaps, used as template to copy to other objects.
        target_objects: Objects to purge and repair UVMaps.

    """
    for obj in target_objects:
        if obj == template_object:
            continue
        while obj.data.uv_layers:
            obj.data.uv_layers.remove(obj.data.uv_layers[0])
        for uvmap in template_object.data.uv_layers:
            obj.data.uv_layers.new(name=repair_uvmap_name(uvmap.name))


def validate_uvmaps_against_valid(valid_obj: Object, test_objs: list[Object]) -> Tuple[bool, str]:
    """Validate a list of objects UV maps against a known valid object.

    Args:
    ----
        valid_obj: Object already tested to have valid UV maps
        test_objs: List of objects to test against valid_obj

    Returns:
    -------
        True if UV maps are valid, info message

    """
    valid_map_names = [uvmap.name for uvmap in valid_obj.data.uv_layers]

    for obj in test_objs:
        obj_maps = [uvmap.name for uvmap in obj.data.uv_layers]
        if obj_maps != valid_map_names:
            return False, (
                f"<{obj.name}> UV sets do not match those of <{valid_obj.name}>. "
                "Each object should have the same UV sets."
            )

    return True, "UV Sets are valid"


def validate_object_names(objs: list[Object], fix_names: bool) -> Tuple[bool, str]:
    """Validate object names for RizomUV when using OBJ format.

    -- Assert that object names do not contain periods
    this is because Rizom converts these to colons which makes name matching hard.

    Args:
    ----
        objs: List of objects to validate names.
        fix_names: True if names should be edited to conform with rules.

    Returns:
    -------
        True if names are valid, info message

    """
    for obj in objs:
        if "." in obj.name:
            if fix_names:
                obj.name = obj.name.replace(".", "_")
            else:
                return False, (
                    f"<{obj.name}> name contains a period. "
                    "When using OBJ or USD format Rizom will edit the object name, "
                    "this complicates name matching on import."
                )
        if obj.name == "DefaultObject":
            return False, f"<{obj.name}> name should not be used with OBJ format"

    return True, "Object names are valid"


def meshes_have_equal_polycount(obj1: Object, obj2: Object) -> bool:
    """Compare the mesh data of two objects.

    Test to make sure two objects have identical poly count.
    This is to check for mesh corruption which can occur with the
    FBX exporter. If mesh topology is not the same then wont be able
    to transfer UV maps.

    Args:
    ----
        obj1: Object to compare
        obj2: Object to compare

    Returns:
    -------
        True if mesh topology is the same

    """
    return len(obj1.data.polygons) == len(obj2.data.polygons)
