from __future__ import annotations

from typing import TYPE_CHECKING, List

from aasm.intermediate.graph import (AgentConstantAmount, AgentPercentAmount,
                                     ConnectionConstantAmount,
                                     ConnectionDistExpAmount,
                                     ConnectionDistNormalAmount,
                                     ConnectionDistUniformAmount,
                                     StatisticalAgent, StatisticalGraph)
from aasm.utils.validation import is_float, is_int

if TYPE_CHECKING:
    from parsing.state import State

    from aasm.intermediate.graph import AgentAmount, ConnectionAmount


def op_DEFG(state: State, agent_name: str, amount: str, args: List[str]) -> None:    
    state.require(
        state.in_graph, 
        'Cannot define agent graph amount outside graph scope.', 
        'Try defining new graphs using GRAPH.'
    )
    state.require(
        isinstance(state.last_graph, StatisticalGraph), 
        'DEFG can be used with statistical graphs.', 
        'Define statistical graphs with GRAPH statistical.'
    )
    state.require(state.agent_exists(agent_name), f'Agent {agent_name} is not defined.')
    
    agent_amount: AgentAmount | None = None
    if amount.endswith('%'):
        percent_value = amount[:-1]
        state.require(is_float(percent_value), f'{amount} is not a valid float.')
        state.require(
            float(percent_value) >= 0 and float(percent_value) <= 100, 
            f'{amount} is not a valid agent amount.', 
            'Amount must be non-negative.'
        )

        agent_amount = AgentPercentAmount(percent_value)

    else:
        state.require(is_int(amount), f'{amount} is not a valid integer.')
        state.require(int(amount) >= 0, f'{amount} is not a valid agent amount.', 'Amount must be non-negative.')

        agent_amount = AgentConstantAmount(amount)
            

    connection_amount: ConnectionAmount | None = None
    match args:
        case [ value ]:
            state.require(is_int(value), f'{value} is not a valid integer.')
            state.require(
                int(value) >= 0, 
                f'{value} is not a valid connection amount.', 
                'Amount must be non-negative.'
            )

            connection_amount = ConnectionConstantAmount(value)

        case [ 'dist_normal', mean, std_dev ]:
            state.require(is_float(mean), f'{mean} is not a valid float.')
            state.require(is_float(std_dev), f'{std_dev} is not a valid float.')
            state.require(
                float(std_dev) >= 0, 
                f'{std_dev} is not a valid standard deviation parameter.', 
                'Standard deviation must be non-negative.'
            )

            connection_amount = ConnectionDistNormalAmount(mean, std_dev)
            
        case [ 'dist_exp', lambda_ ]:
            state.require(is_float(lambda_), f'{lambda_} is not a valid float.')
            state.require(
                float(lambda_) > 0, 
                f'{lambda_} is not a valid lambda parameter.', 
                'Lambda must be positive.'
            )
            
            connection_amount = ConnectionDistExpAmount(lambda_)
            
        case [ 'dist_uniform', a, b ]:
            state.require(is_float(a), f'{a} is not a valid float.')
            state.require(is_float(b), f'{b} is not a valid float.')
            
            connection_amount = ConnectionDistUniformAmount(a, b)

        case _:
            state.panic(f'Incorrect operation: DEFG {agent_name} {amount} {args}')

    state.last_graph.add_agent(StatisticalAgent(agent_name, agent_amount, connection_amount))
