"""
This program has been developed by students from the bachelor Computer Science at Utrecht
University within the Software Project course.
© Copyright Utrecht University (Department of Information and Computing Sciences)
"""

from enum import Enum
from typing import Literal

from pydantic import UUID4, BaseModel, field_validator


class ProgramElement(BaseModel):
    """
    Represents a basic element of our behavior program.

    :ivar name: The researcher-assigned name of the element.
    :ivar id: Unique identifier.
    """

    name: str
    id: UUID4

    # To make program elements hashable
    model_config = {"frozen": True}

    @field_validator("name")
    @classmethod
    def name_must_not_start_with_number(cls, v: str) -> str:
        if v and v[0].isdigit():
            raise ValueError('Field "name" must not start with a number.')
        return v


class LogicalOperator(Enum):
    """
    Logical operators for combining beliefs.

    These operators define how beliefs can be combined to form more complex
    logical conditions. They are used in inferred beliefs to create compound
    beliefs from simpler ones.

    AND: Both operands must be true for the result to be true.
    OR: At least one operand must be true for the result to be true.
    """

    AND = "AND"
    OR = "OR"


type Belief = KeywordBelief | SemanticBelief | InferredBelief | EmotionBelief
type BasicBelief = KeywordBelief | SemanticBelief | EmotionBelief


class KeywordBelief(ProgramElement):
    """
    Represents a belief that is activated when a specific keyword is detected in the user's speech.

    Keyword beliefs provide a simple but effective way to detect specific topics
    or intentions in user speech. They are triggered when the exact keyword
    string appears in the transcribed user input.

    :ivar keyword: The string to look for in the transcription.

    Example:
        A keyword belief with keyword="robot" would be activated when the user
        says "I like the robot" or "Tell me about robots".
    """

    name: str = ""
    keyword: str


class SemanticBelief(ProgramElement):
    """
    Represents a belief whose truth value is determined by an LLM analyzing the conversation
    context.

    Semantic beliefs provide more sophisticated belief detection by using
    an LLM to analyze the conversation context and determine
    if the belief should be considered true. This allows for more nuanced
    and context-aware belief evaluation.

    :ivar description: A natural language description of what this belief represents,
    used as a prompt for the LLM.

    Example:
        A semantic belief with description="The user is expressing frustration"
        would be activated when the LLM determines that the user's statements
        indicate frustration, even if no specific keywords are used.
    """

    description: str


class InferredBelief(ProgramElement):
    """
    Represents a belief derived from other beliefs using logical operators.

    Inferred beliefs allow for the creation of complex belief structures by
    combining simpler beliefs using logical operators. This enables the
    representation of sophisticated conditions and relationships between
    different aspects of the conversation or context.

    :ivar operator: The :class:`LogicalOperator` (AND/OR) to apply.
    :ivar left: The left operand (another belief).
    :ivar right: The right operand (another belief).
    """

    name: str = ""
    operator: LogicalOperator
    left: Belief
    right: Belief


class EmotionBelief(ProgramElement):
    """
    Represents a belief that is set when a certain emotion is detected.

    :ivar emotion: The emotion on which this belief gets set.
    """

    name: str = ""
    emotion: str


class Norm(ProgramElement):
    """
    Base class for behavioral norms that guide the robot's interactions.

    Norms represent guidelines, principles, or rules that should govern the
    robot's behavior during interactions. They can be either basic (always
    active in their phase) or conditional (active only when specific beliefs
    are true).

    :ivar norm: The textual description of the norm.
    :ivar critical: Whether this norm is considered critical and should be strictly enforced.

    Critical norms are currently not supported yet, but are intended for norms that should
    ABSOLUTELY NOT be violated, possible cheched by additional validator agents.
    """

    name: str = ""
    norm: str
    critical: bool = False


class BasicNorm(Norm):
    """
    A simple behavioral norm that is always considered for activation when its phase is active.

    Basic norms are the most straightforward type of norms. They are active
    throughout their assigned phase and provide consistent behavioral guidance
    without any additional conditions.

    These norms are suitable for general principles that should always apply
    during a particular interaction phase.
    """

    pass


class ConditionalNorm(Norm):
    """
    A behavioral norm that is only active when a specific condition (belief) is met.

    Conditional norms provide context-sensitive behavioral guidance. They are
    only active and considered for activation when their associated condition
    (belief) is true. This allows for more nuanced and adaptive behavior that
    responds to the specific context of the interaction.

    An important note, is that the current implementation of these norms for keyword-based beliefs
    is that they only hold for 1 turn, as keyword-based conditions often express temporary
    conditions.

    :ivar condition: The :class:`Belief` that must hold for this norm to be active.

    Example:
        A conditional norm with the condition "user is frustrated" might specify
        that the robot should use more empathetic language and avoid complex topics.
    """

    condition: Belief


type PlanElement = Goal | Action


class Plan(ProgramElement):
    """
    Represents a list of steps to execute. Each of these steps can be a goal (with its own plan)
     or a simple action.

    Plans define sequences of actions and subgoals that the robot should execute
    to achieve a particular objective. They form the procedural knowledge of
    the behavior program, specifying what the robot should do in different
    situations.

    :ivar steps: The actions or subgoals to execute, in order.
    """

    name: str = ""
    steps: list[PlanElement]


class BaseGoal(ProgramElement):
    """
    Represents an objective to be achieved. This base version does not include a plan to achieve
    this goal, and is used in semantic belief extraction.

    :ivar description: A description of the goal, used to determine if it has been achieved.
    :ivar can_fail: Whether we can fail to achieve the goal after executing the plan.

    The can_fail attribute determines whether goal achievement is binary
    (success/failure) or whether it can be determined through conversation
    analysis.
    """

    description: str = ""
    can_fail: bool = True


class Goal(BaseGoal):
    """
    Represents an objective to be achieved. To reach the goal, we should execute the corresponding
    plan. It inherits from the BaseGoal a variable `can_fail`, which, if true, will cause the
    completion to be determined based on the conversation.

    Goals extend base goals by including a specific plan to achieve the objective.
    They form the core of the robot's proactive behavior, defining both what
    should be accomplished and how to accomplish it.

    Instances of this goal are not hashable because a plan is not hashable.

    :ivar plan: The plan to execute.
    """

    plan: Plan


type Action = SpeechAction | GestureAction | LLMAction


class SpeechAction(ProgramElement):
    """
    An action where the robot speaks a predefined literal text.

    :ivar text: The text content to be spoken.
    """

    name: str = ""
    text: str


class Gesture(BaseModel):
    """
    Defines a physical gesture for the robot to perform.

    :ivar type: Whether to use a specific "single" gesture or a random one from a "tag" category.
    :ivar name: The identifier for the gesture or tag.

    The type field determines how the gesture is selected:
    - "single": Use the specific gesture identified by name
    - "tag": Select a random gesture from the category identified by name
    """

    type: Literal["tag", "single"]
    name: str


class GestureAction(ProgramElement):
    """
    An action where the robot performs a physical gesture.

    :ivar gesture: The :class:`Gesture` definition.
    """

    name: str = ""
    gesture: Gesture


class LLMAction(ProgramElement):
    """
    An action that triggers an LLM-generated conversational response.

    :ivar goal: A temporary conversational goal to guide the LLM's response generation.

    The goal parameter provides high-level guidance to the LLM about what
    the response should aim to achieve, while allowing the LLM flexibility
    in how to express it.
    """

    name: str = ""
    goal: str


class Trigger(ProgramElement):
    """
    Defines a reactive behavior: when the condition (belief) is met, the plan is executed.

    :ivar condition: The :class:`Belief` that triggers this behavior.
    :ivar plan: The :class:`Plan` to execute upon activation.
    """

    condition: Belief
    plan: Plan


class Phase(ProgramElement):
    """
    A logical stage in the interaction program, grouping norms, goals, and triggers.

    :ivar norms: List of norms active during this phase.
    :ivar goals: List of goals the robot pursues in this phase.
    :ivar triggers: List of reactive behaviors defined for this phase.
    """

    name: str = ""
    norms: list[BasicNorm | ConditionalNorm]
    goals: list[Goal]
    triggers: list[Trigger]


class Program(BaseModel):
    """
    The top-level container for a complete robot behavior definition.

    The Program class represents the complete specification of a robot's
    behavioral logic. It contains all the phases, norms, goals, triggers,
    and actions that define how the robot should behave during interactions.

    :ivar phases: An ordered list of :class:`Phase` objects defining the interaction flow.
    """

    phases: list[Phase]


if __name__ == "__main__":
    input = input("Enter program JSON: ")
    program = Program.model_validate_json(input)
    print(program)
