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

from dify_graph.enums import NodeExecutionType, NodeType, WorkflowNodeExecutionStatus
from dify_graph.node_events import NodeRunResult
from dify_graph.nodes.answer.entities import AnswerNodeData
from dify_graph.nodes.base.node import Node
from dify_graph.nodes.base.template import Template
from dify_graph.nodes.base.variable_template_parser import VariableTemplateParser
from dify_graph.variables import ArrayFileSegment, FileSegment, Segment


class AnswerNode(Node[AnswerNodeData]):
    node_type = NodeType.ANSWER
    execution_type = NodeExecutionType.RESPONSE

    @classmethod
    def version(cls) -> str:
        return "1"

    def _run(self) -> NodeRunResult:
        segments = self.graph_runtime_state.variable_pool.convert_template(self.node_data.answer)
        files = self._extract_files_from_segments(segments.value)
        return NodeRunResult(
            status=WorkflowNodeExecutionStatus.SUCCEEDED,
            outputs={"answer": segments.markdown, "files": ArrayFileSegment(value=files)},
        )

    def _extract_files_from_segments(self, segments: Sequence[Segment]):
        """Extract all files from segments containing FileSegment or ArrayFileSegment instances.

        FileSegment contains a single file, while ArrayFileSegment contains multiple files.
        This method flattens all files into a single list.
        """
        files = []
        for segment in segments:
            if isinstance(segment, FileSegment):
                # Single file - wrap in list for consistency
                files.append(segment.value)
            elif isinstance(segment, ArrayFileSegment):
                # Multiple files - extend the list
                files.extend(segment.value)
        return files

    @classmethod
    def _extract_variable_selector_to_variable_mapping(
        cls,
        *,
        graph_config: Mapping[str, Any],
        node_id: str,
        node_data: Mapping[str, Any],
    ) -> Mapping[str, Sequence[str]]:
        # Create typed NodeData from dict
        typed_node_data = AnswerNodeData.model_validate(node_data)

        variable_template_parser = VariableTemplateParser(template=typed_node_data.answer)
        variable_selectors = variable_template_parser.extract_variable_selectors()

        variable_mapping = {}
        for variable_selector in variable_selectors:
            variable_mapping[node_id + "." + variable_selector.variable] = variable_selector.value_selector

        return variable_mapping

    def get_streaming_template(self) -> Template:
        """
        Get the template for streaming.

        Returns:
            Template instance for this Answer node
        """
        return Template.from_answer_template(self.node_data.answer)
