"""
Predict Shell - Route Prediction
Standalone version for cubesquare package.
"""

import json
from typing import Dict, Any
from ..circular.state_machine import ILogicState, ILogicStateMachine


class PredictShell:
    """
    Predict Shell.

    Provides route prediction operations:
    - Predict routing outcomes without execution
    - Find resolvers for conditions
    - Detect cycles
    """

    def __init__(self):
        """Initialize Predict shell."""
        self.state_machine = ILogicStateMachine()
        self.states = {
            'DIRECT': ILogicState.DIRECT,
            'INVERSE': ILogicState.INVERSE,
            'SPIRAL': ILogicState.SPIRAL,
            'ANTI_SPIRAL': ILogicState.ANTI_SPIRAL,
            'ABSORBED': ILogicState.ABSORBED,
        }

    def status(self, as_json: bool = False) -> Dict[str, Any]:
        """Get Predict system status."""
        result = {
            'system': 'Predict Shell',
            'version': '1.0.0',
            'thesis': 'Know routing outcomes before execution',
            'complexity': 'O(N) prediction with zero execution cost per hop',
            'status': 'operational',
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def route(self, route_str: str, as_json: bool = False) -> Dict[str, Any]:
        """Predict route outcome."""
        # Parse route: "gateway:DIRECT,firewall:INVERSE,sink:ABSORBED"
        pairs = [p.strip() for p in route_str.split(',')]

        agents = []
        for pair in pairs:
            if ':' not in pair:
                return {'error': f'Invalid format: {pair}. Expected agent:STATE'}
            agent, state = pair.split(':')
            state = state.strip().upper()
            if state not in self.states:
                return {'error': f'Unknown state: {state}'}
            agents.append({'agent': agent.strip(), 'state': state})

        # Simulate signal propagation
        signal = self.states['DIRECT']
        trace = []
        terminated_at = None

        for i, agent in enumerate(agents):
            agent_state = self.states[agent['state']]
            new_signal = self.state_machine.multiply(signal, agent_state)

            trace.append({
                'step': i + 1,
                'agent': agent['agent'],
                'agent_state': agent['state'],
                'signal_before': signal.name,
                'signal_after': new_signal.name,
            })

            signal = new_signal

            if signal == ILogicState.ABSORBED:
                terminated_at = i + 1
                break

        result = {
            'route': route_str,
            'final_state': signal.name,
            'terminated': signal == ILogicState.ABSORBED,
            'terminated_at_step': terminated_at,
            'total_agents': len(agents),
            'steps_executed': len(trace),
            'trace': trace,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def resolve(self, condition: str, as_json: bool = False) -> Dict[str, Any]:
        """Find which agent resolves a condition."""
        condition = condition.upper()
        if condition not in self.states:
            return {'error': f'Unknown condition: {condition}'}

        cond_state = self.states[condition]

        # Find agent that produces DIRECT when multiplied with condition
        resolvers = []
        for agent_name, agent_state in self.states.items():
            result_state = self.state_machine.multiply(cond_state, agent_state)
            if result_state == ILogicState.DIRECT:
                resolvers.append({
                    'agent_state': agent_name,
                    'operation': f'{condition} x {agent_name} = DIRECT',
                    'strength': 1.0,
                })

        result = {
            'condition': condition,
            'resolvers': resolvers,
            'best_resolver': resolvers[0] if resolvers else None,
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def cycle(self, states_str: str, as_json: bool = False) -> Dict[str, Any]:
        """Detect cycle in state sequence."""
        state_names = [s.strip().upper() for s in states_str.split(',')]

        for name in state_names:
            if name not in self.states:
                return {'error': f'Unknown state: {name}'}

        # Compute cumulative product
        products = []
        cumulative = self.states['DIRECT']

        for name in state_names:
            state = self.states[name]
            cumulative = self.state_machine.multiply(cumulative, state)
            products.append(cumulative.name)

        # Check for cycle (returns to DIRECT)
        has_cycle = False
        cycle_period = None

        for i, prod in enumerate(products):
            if prod == 'DIRECT' and i > 0:
                has_cycle = True
                cycle_period = i + 1
                break

        result = {
            'states': state_names,
            'products': products,
            'has_cycle': has_cycle,
            'cycle_period': cycle_period,
            'final_state': products[-1] if products else 'DIRECT',
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def demo(self, as_json: bool = False) -> Dict[str, Any]:
        """Run prediction demos."""
        demos = []

        # Demo 1: Simple route
        route1 = self.route("gateway:DIRECT,firewall:INVERSE,sink:ABSORBED", False)
        demos.append({
            'name': 'Simple Route',
            'description': 'Signal passes through 3 agents',
            'route': "gateway:DIRECT,firewall:INVERSE,sink:ABSORBED",
            'result': route1['final_state'],
            'terminated_at': route1['terminated_at_step'],
        })

        # Demo 2: Resolver
        resolve1 = self.resolve("INVERSE", False)
        demos.append({
            'name': 'Find Resolver',
            'description': 'Find agent that resolves INVERSE condition',
            'condition': 'INVERSE',
            'resolver': resolve1['best_resolver']['agent_state'] if resolve1['best_resolver'] else 'None',
        })

        # Demo 3: Cycle detection
        cycle1 = self.cycle("SPIRAL,SPIRAL,SPIRAL,SPIRAL", False)
        demos.append({
            'name': 'Cycle Detection',
            'description': 'Detect period-4 cycle in SPIRAL chain',
            'states': 'SPIRAL,SPIRAL,SPIRAL,SPIRAL',
            'has_cycle': cycle1['has_cycle'],
            'period': cycle1['cycle_period'],
        })

        result = {
            'demos': demos,
            'count': len(demos),
        }

        if as_json:
            return json.dumps(result, indent=2)
        return result

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a Predict shell command."""
        commands = {
            'status': lambda: self.status(as_json),
            'route': lambda: self.route(args[0], as_json) if args else {'error': 'Route required'},
            'resolve': lambda: self.resolve(args[0], as_json) if args else {'error': 'Condition required'},
            'cycle': lambda: self.cycle(args[0], as_json) if args else {'error': 'States required'},
            'demo': lambda: self.demo(as_json),
        }

        if command in commands:
            return commands[command]()
        else:
            return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}
