from __future__ import annotations

import time

from comfy_api.v0_0_2 import IO


class LongComboDropdown:
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {"option": ([f"Option {i}" for i in range(1_000)],)}}

    RETURN_TYPES = ()
    OUTPUT_NODE = True
    FUNCTION = "long_combo_dropdown"
    CATEGORY = "DevTools"
    DESCRIPTION = "A long combo dropdown"

    def long_combo_dropdown(self, option: str):
        print(option)


class NodeWithOptionalInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {"required_input": ("IMAGE",)},
            "optional": {"optional_input": ("IMAGE", {"default": None})},
        }

    RETURN_TYPES = ("IMAGE",)
    FUNCTION = "node_with_optional_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with an optional input"

    def node_with_optional_input(self, required_input, optional_input=None):
        print(
            f"Calling node with required_input: {required_input} and optional_input: {optional_input}"
        )
        return (required_input,)


class NodeWithOptionalComboInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "optional": {
                "optional_combo_input": (
                    [f"Random Unique Option {time.time()}" for _ in range(8)],
                    {"default": None},
                )
            },
        }

    RETURN_TYPES = ("STRING",)
    FUNCTION = "node_with_optional_combo_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with an optional combo input that returns unique values every time INPUT_TYPES is called"

    def node_with_optional_combo_input(self, optional_combo_input=None):
        print(f"Calling node with optional_combo_input: {optional_combo_input}")
        return (optional_combo_input,)


class NodeWithOnlyOptionalInput:
    @classmethod
    def INPUT_TYPES(s):
        return {
            "optional": {
                "text": ("STRING", {"multiline": True, "dynamicPrompts": True}),
                "clip": ("CLIP", {}),
            }
        }

    RETURN_TYPES = ()
    FUNCTION = "node_with_only_optional_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with only optional input"

    def node_with_only_optional_input(self, clip=None, text=None):
        pass


class NodeWithOutputList:
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {}}

    RETURN_TYPES = (
        "INT",
        "INT",
    )
    RETURN_NAMES = (
        "INTEGER OUTPUT",
        "INTEGER LIST OUTPUT",
    )
    OUTPUT_IS_LIST = (
        False,
        True,
    )
    FUNCTION = "node_with_output_list"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with an output list"

    def node_with_output_list(self):
        return (1, [1, 2, 3])


class NodeWithForceInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "int_input": ("INT", {"forceInput": True}),
                "int_input_widget": ("INT", {"default": 1}),
            },
            "optional": {"float_input": ("FLOAT", {"forceInput": True})},
        }

    RETURN_TYPES = ()
    OUTPUT_NODE = True
    FUNCTION = "node_with_force_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a forced input"

    def node_with_force_input(
        self, int_input: int, int_input_widget: int, float_input: float = 0.0
    ):
        print(
            f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
        )


class NodeWithDefaultInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "int_input": ("INT", {"defaultInput": True}),
                "int_input_widget": ("INT", {"default": 1}),
            },
            "optional": {"float_input": ("FLOAT", {"defaultInput": True})},
        }

    RETURN_TYPES = ()
    OUTPUT_NODE = True
    FUNCTION = "node_with_default_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a default input"

    def node_with_default_input(
        self, int_input: int, int_input_widget: int, float_input: float = 0.0
    ):
        print(
            f"int_input: {int_input}, int_input_widget: {int_input_widget}, float_input: {float_input}"
        )


class NodeWithStringInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {"string_input": ("STRING",)}}

    RETURN_TYPES = ()
    FUNCTION = "node_with_string_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a string input"

    def node_with_string_input(self, string_input: str):
        print(f"string_input: {string_input}")


class NodeWithUnionInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "optional": {
                "string_or_int_input": ("STRING,INT",),
                "string_input": ("STRING", {"forceInput": True}),
                "int_input": ("INT", {"forceInput": True}),
            }
        }

    RETURN_TYPES = ()
    OUTPUT_NODE = True
    FUNCTION = "node_with_union_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a union input"

    def node_with_union_input(
        self,
        string_or_int_input: str | int = "",
        string_input: str = "",
        int_input: int = 0,
    ):
        print(
            f"string_or_int_input: {string_or_int_input}, string_input: {string_input}, int_input: {int_input}"
        )
        return {
            "ui": {
                "text": string_or_int_input,
            }
        }


class NodeWithBooleanInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {"boolean_input": ("BOOLEAN",)}}

    RETURN_TYPES = ()
    FUNCTION = "node_with_boolean_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a boolean input"

    def node_with_boolean_input(self, boolean_input: bool):
        print(f"boolean_input: {boolean_input}")


class SimpleSlider:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "value": (
                    "FLOAT",
                    {
                        "display": "slider",
                        "default": 0.5,
                        "min": 0.0,
                        "max": 1.0,
                        "step": 0.001,
                    },
                ),
            },
        }

    RETURN_TYPES = ("FLOAT",)
    FUNCTION = "execute"
    CATEGORY = "DevTools"

    def execute(self, value):
        return (value,)


class NodeWithSeedInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {"required": {"seed": ("INT", {"default": 0})}}

    RETURN_TYPES = ()
    FUNCTION = "node_with_seed_input"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node with a seed input"
    OUTPUT_NODE = True

    def node_with_seed_input(self, seed: int):
        print(f"seed: {seed}")


class NodeWithValidation:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {"int_input": ("INT",)},
        }

    @classmethod
    def VALIDATE_INPUTS(cls, int_input: int):
        if int_input < 0:
            raise ValueError("int_input must be greater than 0")
        return True

    RETURN_TYPES = ()
    FUNCTION = "execute"
    CATEGORY = "DevTools"
    DESCRIPTION = "A node that validates an input"
    OUTPUT_NODE = True

    def execute(self, int_input: int):
        print(f"int_input: {int_input}")
        return tuple()


class NodeWithV2ComboInput:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": {
                "combo_input": (
                    "COMBO",
                    {"options": ["A", "B"]},
                ),
            }
        }

    RETURN_TYPES = ("COMBO",)
    FUNCTION = "node_with_v2_combo_input"
    CATEGORY = "DevTools"
    DESCRIPTION = (
        "A node that outputs a combo type that adheres to the v2 combo input spec"
    )

    def node_with_v2_combo_input(self, combo_input: str):
        return (combo_input,)

class NodeWithLegacyWidget:
    @classmethod
    def INPUT_TYPES(cls):
        return {
            "required": { "legacy_widget": ("INT", { "widgetType":  "DEVTOOLSLEGACYWIDGET" }) }
        }

    RETURN_TYPES = ()
    FUNCTION = "node_with_legacy_widget"
    CATEGORY = "DevTools"
    DESCRIPTION = ("A node with a legacy widget")

    def node_with_legacy_widget(self):
        return ()

class NodeWithPriceBadge(IO.ComfyNode):
    @classmethod
    def define_schema(cls):
        return IO.Schema(
            node_id="DevToolsNodeWithPriceBadge",
            display_name="Node With Price Badge",
            description="An API node with a price badge",
            inputs=[IO.Combo.Input("price", options=["1x", "2x", "3x"])],
            is_api_node=True,
            price_badge=IO.PriceBadge(
                depends_on=IO.PriceBadgeDepends(widgets=["price"]),
                expr="""
                (
                  $p := widgets.price;
                  {"type":"usd","usd": $contains($p, "2x") ? 2 : $contains($p, "3x") ? 3 : 1}
                )
                """,
            ),
        )

    @classmethod
    async def execute(cls, price):
        return IO.NodeOutput()


NODE_CLASS_MAPPINGS = {
    "DevToolsLongComboDropdown": LongComboDropdown,
    "DevToolsNodeWithOptionalInput": NodeWithOptionalInput,
    "DevToolsNodeWithOptionalComboInput": NodeWithOptionalComboInput,
    "DevToolsNodeWithOnlyOptionalInput": NodeWithOnlyOptionalInput,
    "DevToolsNodeWithOutputList": NodeWithOutputList,
    "DevToolsNodeWithForceInput": NodeWithForceInput,
    "DevToolsNodeWithDefaultInput": NodeWithDefaultInput,
    "DevToolsNodeWithStringInput": NodeWithStringInput,
    "DevToolsNodeWithUnionInput": NodeWithUnionInput,
    "DevToolsNodeWithBooleanInput": NodeWithBooleanInput,
    "DevToolsSimpleSlider": SimpleSlider,
    "DevToolsNodeWithSeedInput": NodeWithSeedInput,
    "DevToolsNodeWithValidation": NodeWithValidation,
    "DevToolsNodeWithV2ComboInput": NodeWithV2ComboInput,
    "DevToolsNodeWithLegacyWidget": NodeWithLegacyWidget,
    "DevToolsNodeWithPriceBadge": NodeWithPriceBadge,
}

NODE_DISPLAY_NAME_MAPPINGS = {
    "DevToolsLongComboDropdown": "Long Combo Dropdown",
    "DevToolsNodeWithOptionalInput": "Node With Optional Input",
    "DevToolsNodeWithOptionalComboInput": "Node With Optional Combo Input",
    "DevToolsNodeWithOnlyOptionalInput": "Node With Only Optional Input",
    "DevToolsNodeWithOutputList": "Node With Output List",
    "DevToolsNodeWithForceInput": "Node With Force Input",
    "DevToolsNodeWithDefaultInput": "Node With Default Input",
    "DevToolsNodeWithStringInput": "Node With String Input",
    "DevToolsNodeWithUnionInput": "Node With Union Input",
    "DevToolsNodeWithBooleanInput": "Node With Boolean Input",
    "DevToolsSimpleSlider": "Simple Slider",
    "DevToolsNodeWithSeedInput": "Node With Seed Input",
    "DevToolsNodeWithValidation": "Node With Validation",
    "DevToolsNodeWithV2ComboInput": "Node With V2 Combo Input",
    "DevToolsNodeWithLegacyWidget": "Node With Legacy Widget",
    "DevToolsNodeWithPriceBadge": "Node With Price Badge",
}

__all__ = [
    "LongComboDropdown",
    "NodeWithOptionalInput",
    "NodeWithOptionalComboInput",
    "NodeWithOnlyOptionalInput",
    "NodeWithOutputList",
    "NodeWithForceInput",
    "NodeWithDefaultInput",
    "NodeWithStringInput",
    "NodeWithUnionInput",
    "NodeWithBooleanInput",
    "SimpleSlider",
    "NodeWithSeedInput",
    "NodeWithValidation",
    "NodeWithV2ComboInput",
    "NODE_CLASS_MAPPINGS",
    "NODE_DISPLAY_NAME_MAPPINGS",
]
