from __future__ import annotations

import json

try:
    from .character_config import (
        build_characteristics_config_json,
        build_hair_config_json,
        character_age_choices,
        character_body_choices,
        character_descriptor_detail_choices,
        character_eye_color_choices,
        character_figure_choices,
        character_hair_color_choices,
        character_hair_length_choices,
        character_hair_style_choices,
        character_label_choices,
        character_man_body_choices,
        character_presence_choices,
        character_woman_body_choices,
    )
    from .character_profile import (
        build_character_manual_config_json,
        character_profile_choices,
        load_character_profile_json,
    )
    from .character_slot import build_character_slot_json
    from .prompt_builder import (
        build_character_profile_json,
        character_ethnicity_choices,
        character_hardcore_clothing_state_choices,
        character_hardcore_clothing_values,
        character_softcore_outfit_source_choices,
        character_softcore_outfit_values,
    )
except ImportError:  # Allows local smoke tests from the repository root.
    from character_config import (
        build_characteristics_config_json,
        build_hair_config_json,
        character_age_choices,
        character_body_choices,
        character_descriptor_detail_choices,
        character_eye_color_choices,
        character_figure_choices,
        character_hair_color_choices,
        character_hair_length_choices,
        character_hair_style_choices,
        character_label_choices,
        character_man_body_choices,
        character_presence_choices,
        character_woman_body_choices,
    )
    from character_profile import (
        build_character_manual_config_json,
        character_profile_choices,
        load_character_profile_json,
    )
    from character_slot import build_character_slot_json
    from prompt_builder import (
        build_character_profile_json,
        character_ethnicity_choices,
        character_hardcore_clothing_state_choices,
        character_hardcore_clothing_values,
        character_softcore_outfit_source_choices,
        character_softcore_outfit_values,
    )


SXCP_HAIR_CONFIG = "SXCP_HAIR_CONFIG"
SXCP_CHARACTERISTICS = "SXCP_CHARACTERISTICS"
SXCP_CHARACTER_MANUAL = "SXCP_CHARACTER_MANUAL"
SXCP_ETHNICITY_LIST = "SXCP_ETHNICITY_LIST"
SXCP_CHARACTER_CAST = "SXCP_CHARACTER_CAST"
SXCP_CHARACTER_SLOT = "SXCP_CHARACTER_SLOT"
SXCP_CHARACTER_PROFILE = "SXCP_CHARACTER_PROFILE"


class _SxCPHairAxisNode:
    AXIS = "color"
    PREFIX = "include"

    @classmethod
    def _choices(cls):
        if cls.AXIS == "color":
            return [choice for choice in character_hair_color_choices() if choice != "random"]
        if cls.AXIS == "length":
            return [choice for choice in character_hair_length_choices() if choice != "random"]
        return [choice for choice in character_hair_style_choices() if choice != "random"]

    @classmethod
    def INPUT_TYPES(cls):
        required = {
            "combine_mode": (["replace_axis", "add_to_axis"], {"default": "replace_axis"}),
        }
        for choice in cls._choices():
            required[f"{cls.PREFIX}_{choice}"] = ("BOOLEAN", {"default": False})
        return {
            "required": required,
            "optional": {
                "hair_config": (SXCP_HAIR_CONFIG,),
            },
        }

    RETURN_TYPES = (SXCP_HAIR_CONFIG, "STRING")
    RETURN_NAMES = ("hair_config", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(self, combine_mode="replace_axis", hair_config="", **kwargs):
        selected = [
            choice
            for choice in self._choices()
            if bool(kwargs.get(f"{self.PREFIX}_{choice}", False))
        ]
        config = build_hair_config_json(
            hair_config=hair_config or "",
            axis=self.AXIS,
            selected_values=selected,
            combine_mode=combine_mode,
        )
        parsed = json.loads(config)
        return config, parsed.get("summary", "")


class SxCPHairColor(_SxCPHairAxisNode):
    AXIS = "color"


class SxCPHairLength(_SxCPHairAxisNode):
    AXIS = "length"


class SxCPHairStyle(_SxCPHairAxisNode):
    AXIS = "style"


def _choice_input_key(prefix, choice):
    key = "".join(char if char.isalnum() else "_" for char in str(choice).lower()).strip("_")
    while "__" in key:
        key = key.replace("__", "_")
    return f"{prefix}_{key}"


class SxCPCharacterAgeRange:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "combine_mode": (["replace_axis", "add_to_axis"], {"default": "replace_axis"}),
                "min_age": ("INT", {"default": 21, "min": 21, "max": 85, "step": 1}),
                "max_age": ("INT", {"default": 35, "min": 21, "max": 85, "step": 1}),
            },
            "optional": {
                "characteristics": (SXCP_CHARACTERISTICS,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTERISTICS, "STRING")
    RETURN_NAMES = ("characteristics", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(self, combine_mode, min_age, max_age, characteristics=""):
        start = max(21, min(85, int(min_age)))
        end = max(21, min(85, int(max_age)))
        if end < start:
            start, end = end, start
        ages = [f"{age}-year-old adult" for age in range(start, end + 1)]
        config = build_characteristics_config_json(
            characteristics=characteristics or "",
            axis="ages",
            selected_values=ages,
            combine_mode=combine_mode,
        )
        return config, json.loads(config).get("summary", "")


class _SxCPBodyPoolNode:
    SUBJECT = "character"

    @classmethod
    def _choices(cls):
        if cls.SUBJECT == "woman":
            return [choice for choice in character_woman_body_choices() if choice not in ("random", "manual")]
        if cls.SUBJECT == "man":
            return [choice for choice in character_man_body_choices() if choice not in ("random", "manual")]
        return [choice for choice in character_body_choices() if choice not in ("random", "manual")]

    @classmethod
    def INPUT_TYPES(cls):
        required = {
            "combine_mode": (["replace_axis", "add_to_axis"], {"default": "replace_axis"}),
        }
        for choice in cls._choices():
            required[_choice_input_key("include", choice)] = ("BOOLEAN", {"default": False})
        return {
            "required": required,
            "optional": {
                "characteristics": (SXCP_CHARACTERISTICS,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTERISTICS, "STRING")
    RETURN_NAMES = ("characteristics", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(self, combine_mode="replace_axis", characteristics="", **kwargs):
        selected = [
            choice
            for choice in self._choices()
            if bool(kwargs.get(_choice_input_key("include", choice), False))
        ]
        config = build_characteristics_config_json(
            characteristics=characteristics or "",
            axis="bodies",
            selected_values=selected,
            combine_mode=combine_mode,
        )
        return config, json.loads(config).get("summary", "")


class SxCPCharacterBodyPool(_SxCPBodyPoolNode):
    SUBJECT = "character"


class SxCPWomanBodyPool(_SxCPBodyPoolNode):
    SUBJECT = "woman"


class SxCPManBodyPool(_SxCPBodyPoolNode):
    SUBJECT = "man"


class SxCPEyeColorPool:
    @classmethod
    def INPUT_TYPES(cls):
        required = {
            "combine_mode": (["replace_axis", "add_to_axis"], {"default": "replace_axis"}),
        }
        for choice in character_eye_color_choices():
            if choice != "random":
                required[_choice_input_key("include", choice)] = ("BOOLEAN", {"default": False})
        return {
            "required": required,
            "optional": {
                "characteristics": (SXCP_CHARACTERISTICS,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTERISTICS, "STRING")
    RETURN_NAMES = ("characteristics", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(self, combine_mode="replace_axis", characteristics="", **kwargs):
        selected = [
            choice
            for choice in character_eye_color_choices()
            if choice != "random" and bool(kwargs.get(_choice_input_key("include", choice), False))
        ]
        config = build_characteristics_config_json(
            characteristics=characteristics or "",
            axis="eyes",
            selected_values=selected,
            combine_mode=combine_mode,
        )
        return config, json.loads(config).get("summary", "")


class SxCPCharacterClothing:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "combine_mode": (["replace_axis", "add_to_axis"], {"default": "replace_axis"}),
                "softcore_source": (character_softcore_outfit_source_choices(), {"default": "no_change"}),
                "hardcore_state": (character_hardcore_clothing_state_choices(), {"default": "no_change"}),
                "custom_softcore_outfits": ("STRING", {"default": "", "multiline": True}),
                "custom_hardcore_clothing": ("STRING", {"default": "", "multiline": True}),
            },
            "optional": {
                "characteristics": (SXCP_CHARACTERISTICS,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTERISTICS, "STRING")
    RETURN_NAMES = ("characteristics", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        combine_mode,
        softcore_source,
        hardcore_state,
        custom_softcore_outfits,
        custom_hardcore_clothing,
        characteristics="",
    ):
        config = characteristics or ""
        if softcore_source != "no_change":
            config = build_characteristics_config_json(
                characteristics=config,
                axis="softcore_outfits",
                selected_values=character_softcore_outfit_values(softcore_source, custom_softcore_outfits),
                combine_mode=combine_mode,
            )
        if hardcore_state != "no_change":
            config = build_characteristics_config_json(
                characteristics=config,
                axis="hardcore_clothing",
                selected_values=character_hardcore_clothing_values(hardcore_state, custom_hardcore_clothing),
                combine_mode=combine_mode,
            )
        if not config:
            config = build_characteristics_config_json(axis="", selected_values=[])
        return config, json.loads(config).get("summary", "")


class SxCPCharacterManualDetails:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "combine_mode": (["merge_nonempty", "replace_all"], {"default": "merge_nonempty"}),
                "manual_age": ("STRING", {"default": ""}),
                "manual_body": ("STRING", {"default": ""}),
                "body_phrase": ("STRING", {"default": ""}),
                "skin": ("STRING", {"default": ""}),
                "hair": ("STRING", {"default": ""}),
                "eyes": ("STRING", {"default": ""}),
                "softcore_outfit": ("STRING", {"default": ""}),
                "hardcore_clothing": ("STRING", {"default": ""}),
            },
            "optional": {
                "manual": (SXCP_CHARACTER_MANUAL,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_MANUAL, "STRING")
    RETURN_NAMES = ("manual", "summary")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        combine_mode,
        manual_age,
        manual_body,
        body_phrase,
        skin,
        hair,
        eyes,
        softcore_outfit,
        hardcore_clothing,
        manual="",
    ):
        config = build_character_manual_config_json(
            manual=manual or "",
            combine_mode=combine_mode,
            manual_age=manual_age,
            manual_body=manual_body,
            body_phrase=body_phrase,
            skin=skin,
            hair=hair,
            eyes=eyes,
            softcore_outfit=softcore_outfit,
            hardcore_clothing=hardcore_clothing,
        )
        parsed = json.loads(config)
        return config, parsed.get("summary", "")


class SxCPCharacterSlot:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "enabled": ("BOOLEAN", {"default": True}),
                "subject_type": (["woman", "man"], {"default": "woman"}),
                "label": (character_label_choices(), {"default": "auto_chain"}),
                "slot_seed": ("INT", {"default": -1, "min": -1, "max": 0xFFFFFFFF}),
                "age": ([choice for choice in character_age_choices() if choice != "manual"], {"default": "random"}),
                "ethnicity": (character_ethnicity_choices(), {"default": "random"}),
                "figure": (character_figure_choices(), {"default": "random"}),
                "body": ([choice for choice in character_body_choices() if choice != "manual"], {"default": "random"}),
                "descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
                "expression_enabled": ("BOOLEAN", {"default": True}),
                "expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "presence_mode": (character_presence_choices(), {"default": "visible"}),
                "softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
            },
            "optional": {
                "manual": (SXCP_CHARACTER_MANUAL,),
                "ethnicity_list": (SXCP_ETHNICITY_LIST,),
                "characteristics": (SXCP_CHARACTERISTICS,),
                "hair_config": (SXCP_HAIR_CONFIG,),
                "character_cast": (SXCP_CHARACTER_CAST,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_CAST, SXCP_CHARACTER_SLOT, "STRING", "STRING")
    RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        enabled,
        subject_type,
        label,
        slot_seed,
        age,
        ethnicity,
        figure,
        body,
        descriptor_detail="auto",
        expression_enabled=True,
        expression_intensity=-1.0,
        presence_mode="visible",
        softcore_expression_intensity=-1.0,
        hardcore_expression_intensity=-1.0,
        character_cast="",
        ethnicity_list="",
        characteristics="",
        hair_config="",
        manual="",
    ):
        result = build_character_slot_json(
            subject_type=subject_type,
            label=label,
            slot_seed=slot_seed,
            age=age,
            manual=manual,
            ethnicity=ethnicity_list or ethnicity,
            figure=figure,
            body=body,
            manual_body="",
            body_phrase="",
            skin="",
            hair="",
            characteristics=characteristics,
            hair_config=hair_config,
            eyes="",
            descriptor_detail=descriptor_detail,
            expression_enabled=expression_enabled,
            expression_intensity=expression_intensity,
            presence_mode=presence_mode,
            softcore_expression_intensity=softcore_expression_intensity,
            hardcore_expression_intensity=hardcore_expression_intensity,
            softcore_outfit="",
            hardcore_clothing="",
            enabled=enabled,
            character_cast=character_cast or "",
        )
        return result["character_cast"], result["character_slot"], result["summary"], result["status"]


class SxCPWomanSlot:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "enabled": ("BOOLEAN", {"default": True}),
                "label": (character_label_choices(), {"default": "auto_chain"}),
                "slot_seed": ("INT", {"default": -1, "min": -1, "max": 0xFFFFFFFF}),
                "age": ([choice for choice in character_age_choices() if choice != "manual"], {"default": "random"}),
                "ethnicity": (character_ethnicity_choices(), {"default": "random"}),
                "figure_bias": (character_figure_choices(), {"default": "random"}),
                "body": ([choice for choice in character_woman_body_choices() if choice != "manual"], {"default": "random"}),
                "descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
                "expression_enabled": ("BOOLEAN", {"default": True}),
                "expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
            },
            "optional": {
                "manual": (SXCP_CHARACTER_MANUAL,),
                "ethnicity_list": (SXCP_ETHNICITY_LIST,),
                "characteristics": (SXCP_CHARACTERISTICS,),
                "hair_config": (SXCP_HAIR_CONFIG,),
                "character_cast": (SXCP_CHARACTER_CAST,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_CAST, SXCP_CHARACTER_SLOT, "STRING", "STRING")
    RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        enabled,
        label,
        slot_seed,
        age,
        ethnicity,
        figure_bias,
        body,
        descriptor_detail="auto",
        expression_enabled=True,
        expression_intensity=-1.0,
        softcore_expression_intensity=-1.0,
        hardcore_expression_intensity=-1.0,
        character_cast="",
        ethnicity_list="",
        characteristics="",
        hair_config="",
        manual="",
    ):
        result = build_character_slot_json(
            subject_type="woman",
            label=label,
            slot_seed=slot_seed,
            age=age,
            manual=manual,
            ethnicity=ethnicity_list or ethnicity,
            figure=figure_bias,
            body=body,
            manual_body="",
            body_phrase="",
            skin="",
            hair="",
            characteristics=characteristics,
            hair_config=hair_config,
            eyes="",
            descriptor_detail=descriptor_detail,
            expression_enabled=expression_enabled,
            expression_intensity=expression_intensity,
            softcore_expression_intensity=softcore_expression_intensity,
            hardcore_expression_intensity=hardcore_expression_intensity,
            softcore_outfit="",
            hardcore_clothing="",
            enabled=enabled,
            character_cast=character_cast or "",
        )
        return result["character_cast"], result["character_slot"], result["summary"], result["status"]


class SxCPManSlot:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "enabled": ("BOOLEAN", {"default": True}),
                "label": (character_label_choices(), {"default": "auto_chain"}),
                "slot_seed": ("INT", {"default": -1, "min": -1, "max": 0xFFFFFFFF}),
                "age": ([choice for choice in character_age_choices() if choice != "manual"], {"default": "random"}),
                "ethnicity": (character_ethnicity_choices(), {"default": "random"}),
                "body": ([choice for choice in character_man_body_choices() if choice != "manual"], {"default": "random"}),
                "descriptor_detail": (character_descriptor_detail_choices(), {"default": "compact"}),
                "expression_enabled": ("BOOLEAN", {"default": True}),
                "expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "presence_mode": (character_presence_choices(), {"default": "visible"}),
                "softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
                "hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
            },
            "optional": {
                "manual": (SXCP_CHARACTER_MANUAL,),
                "ethnicity_list": (SXCP_ETHNICITY_LIST,),
                "characteristics": (SXCP_CHARACTERISTICS,),
                "hair_config": (SXCP_HAIR_CONFIG,),
                "character_cast": (SXCP_CHARACTER_CAST,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_CAST, SXCP_CHARACTER_SLOT, "STRING", "STRING")
    RETURN_NAMES = ("character_cast", "character_slot", "summary", "status")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        enabled,
        label,
        slot_seed,
        age,
        ethnicity,
        body,
        descriptor_detail="compact",
        expression_enabled=True,
        expression_intensity=-1.0,
        presence_mode="visible",
        softcore_expression_intensity=-1.0,
        hardcore_expression_intensity=-1.0,
        character_cast="",
        ethnicity_list="",
        characteristics="",
        hair_config="",
        manual="",
    ):
        result = build_character_slot_json(
            subject_type="man",
            label=label,
            slot_seed=slot_seed,
            age=age,
            manual=manual,
            ethnicity=ethnicity_list or ethnicity,
            figure="random",
            body=body,
            manual_body="",
            body_phrase="",
            skin="",
            hair="",
            characteristics=characteristics,
            hair_config=hair_config,
            eyes="",
            descriptor_detail=descriptor_detail,
            expression_enabled=expression_enabled,
            expression_intensity=expression_intensity,
            presence_mode=presence_mode,
            softcore_expression_intensity=softcore_expression_intensity,
            hardcore_expression_intensity=hardcore_expression_intensity,
            softcore_outfit="",
            hardcore_clothing="",
            enabled=enabled,
            character_cast=character_cast or "",
        )
        return result["character_cast"], result["character_slot"], result["summary"], result["status"]


class SxCPCharacterProfileSave:
    OUTPUT_NODE = True

    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "profile_name": ("STRING", {"default": "saved_character"}),
                "source": (["metadata_json", "character_slot", "manual"], {"default": "metadata_json"}),
                "subject_type": (["woman", "man"], {"default": "woman"}),
                "age": ("STRING", {"default": ""}),
                "body": ("STRING", {"default": ""}),
                "body_phrase": ("STRING", {"default": ""}),
                "skin": ("STRING", {"default": ""}),
                "hair": ("STRING", {"default": ""}),
                "eyes": ("STRING", {"default": ""}),
                "figure": ("STRING", {"default": ""}),
                "save_now": ("BOOLEAN", {"default": False}),
            },
            "optional": {
                "metadata_json": ("STRING", {"default": "", "multiline": True}),
                "character_slot": (SXCP_CHARACTER_SLOT,),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_PROFILE, "STRING", "STRING", "STRING", "STRING", SXCP_CHARACTER_PROFILE)
    RETURN_NAMES = ("character_profile", "descriptor", "profile_name", "saved_path", "status", "profile_json")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        profile_name,
        source,
        subject_type,
        age,
        body,
        body_phrase,
        skin,
        hair,
        eyes,
        figure,
        save_now,
        metadata_json="",
        character_slot="",
    ):
        profile = build_character_profile_json(
            profile_name=profile_name,
            source=source,
            metadata_json=metadata_json or "",
            character_slot=character_slot or "",
            subject_type=subject_type,
            age=age,
            body=body,
            body_phrase=body_phrase,
            skin=skin,
            hair=hair,
            eyes=eyes,
            figure=figure,
            save_now=save_now,
        )
        result = (
            profile["profile_json"],
            profile["descriptor"],
            profile["profile_name"],
            profile["saved_path"],
            profile["status"],
            profile["profile_json"],
        )
        return {
            "ui": {
                "profile_json": [profile["profile_json"]],
                "descriptor": [profile["descriptor"]],
                "profile_name": [profile["profile_name"]],
                "saved_path": [profile["saved_path"]],
                "status": [profile["status"]],
            },
            "result": result,
        }


class SxCPCharacterProfileLoad:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "enabled": ("BOOLEAN", {"default": True}),
                "profile_name": (character_profile_choices(), {"default": "manual"}),
                "rename_to": ("STRING", {"default": ""}),
                "delete_now": ("BOOLEAN", {"default": False}),
                "rename_now": ("BOOLEAN", {"default": False}),
            },
            "optional": {
                "manual_profile_name": ("STRING", {"default": ""}),
                "fallback_profile_json": (SXCP_CHARACTER_PROFILE,),
                "override_subject_type": (["keep_profile", "woman", "man"], {"default": "keep_profile"}),
                "override_age": ("STRING", {"default": ""}),
                "override_body": ("STRING", {"default": ""}),
                "override_body_phrase": ("STRING", {"default": ""}),
                "override_skin": ("STRING", {"default": ""}),
                "override_hair": ("STRING", {"default": ""}),
                "override_eyes": ("STRING", {"default": ""}),
                "override_figure": ("STRING", {"default": ""}),
                "override_descriptor_detail": (["keep_profile"] + character_descriptor_detail_choices(), {"default": "keep_profile"}),
            },
        }

    RETURN_TYPES = (SXCP_CHARACTER_PROFILE, "STRING", "STRING", "STRING", "STRING", SXCP_CHARACTER_PROFILE)
    RETURN_NAMES = ("character_profile", "descriptor", "profile_name", "saved_path", "status", "profile_json")
    FUNCTION = "build"
    CATEGORY = "prompt_builder"

    def build(
        self,
        enabled,
        profile_name,
        rename_to,
        delete_now,
        rename_now,
        manual_profile_name="",
        fallback_profile_json="",
        override_subject_type="keep_profile",
        override_age="",
        override_body="",
        override_body_phrase="",
        override_skin="",
        override_hair="",
        override_eyes="",
        override_figure="",
        override_descriptor_detail="keep_profile",
    ):
        chosen_name = manual_profile_name.strip() if profile_name == "manual" and manual_profile_name.strip() else profile_name
        profile = load_character_profile_json(
            profile_name=chosen_name,
            fallback_profile_json=fallback_profile_json or "",
            enabled=enabled,
            delete_now=delete_now,
            rename_now=rename_now,
            rename_to=rename_to,
            override_subject_type=override_subject_type,
            override_age=override_age,
            override_body=override_body,
            override_body_phrase=override_body_phrase,
            override_skin=override_skin,
            override_hair=override_hair,
            override_eyes=override_eyes,
            override_figure=override_figure,
            override_descriptor_detail=override_descriptor_detail,
        )
        return (
            profile["profile_json"],
            profile["descriptor"],
            profile["profile_name"],
            profile["saved_path"],
            profile["status"],
            profile["profile_json"],
        )


NODE_CLASS_MAPPINGS = {
    "SxCPHairLength": SxCPHairLength,
    "SxCPHairColor": SxCPHairColor,
    "SxCPHairStyle": SxCPHairStyle,
    "SxCPCharacterAgeRange": SxCPCharacterAgeRange,
    "SxCPCharacterBodyPool": SxCPCharacterBodyPool,
    "SxCPWomanBodyPool": SxCPWomanBodyPool,
    "SxCPManBodyPool": SxCPManBodyPool,
    "SxCPEyeColorPool": SxCPEyeColorPool,
    "SxCPCharacterClothing": SxCPCharacterClothing,
    "SxCPCharacterManualDetails": SxCPCharacterManualDetails,
    "SxCPWomanSlot": SxCPWomanSlot,
    "SxCPManSlot": SxCPManSlot,
    "SxCPCharacterSlot": SxCPCharacterSlot,
    "SxCPCharacterProfileSave": SxCPCharacterProfileSave,
    "SxCPCharacterProfileLoad": SxCPCharacterProfileLoad,
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "SxCPHairLength": "SxCP Hair Length",
    "SxCPHairColor": "SxCP Hair Color",
    "SxCPHairStyle": "SxCP Hair Style/Cut",
    "SxCPCharacterAgeRange": "SxCP Character Age Range",
    "SxCPCharacterBodyPool": "SxCP Character Body Pool",
    "SxCPWomanBodyPool": "SxCP Woman Body Pool",
    "SxCPManBodyPool": "SxCP Man Body Pool",
    "SxCPEyeColorPool": "SxCP Eye Color Pool",
    "SxCPCharacterClothing": "SxCP Character Clothing",
    "SxCPCharacterManualDetails": "SxCP Character Manual Details",
    "SxCPWomanSlot": "SxCP Woman Slot",
    "SxCPManSlot": "SxCP Man Slot",
    "SxCPCharacterSlot": "SxCP Character Slot",
    "SxCPCharacterProfileSave": "SxCP Character Profile Save",
    "SxCPCharacterProfileLoad": "SxCP Character Profile Load",
}
