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

LEVERAGES SYSTEM PRIMITIVES:
- Uses cubesquare.i_logic.algebra for O(1) composition
- Uses cubesquare.i_logic.states for complex number identity
- NO local reinvention of physics
"""

import json
from typing import Dict, Any, List

from ..i_logic.states import ILogicState
from ..i_logic.algebra import multiply, compose, detect_cycle


class PredictShell:
    """
    Predict Shell.

    Delegates to Verified System Primitives:
    - ILogicState: Complex number identities
    - i_logic.algebra: Validated multiplication table
    """

    def __init__(self):
        """Initialize Predict shell using system states."""
        # Use the authoritative state definitions
        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": "2.1.0",
            "thesis": "System-native algebraic prediction",
            "mechanism": "Delegates to i_logic.algebra (Verified)",
            "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 using system algebra."""
        try:
            # Parse using system states
            pairs = [p.strip().split(":") for p in route_str.split(",")]
            agent_states = []
            for p in pairs:
                if len(p) != 2:
                    return {"error": f"Invalid format: {p}"}
                name, state_str = p
                state_upper = state_str.strip().upper()
                if state_upper not in self.states:
                    return {"error": f"Unknown state: {state_upper}"}
                agent_states.append((name.strip(), self.states[state_upper]))
        except Exception as e:
            return {"error": f"Parse error: {str(e)}"}

        if not agent_states:
            return {"error": "Empty route"}

        # Use system primitive for composition
        # compose() handles the algebraic reduction
        complex_states = [s for _, s in agent_states]
        final_state, steps = compose(complex_states)

        # Generate trace for visibility (optional, but useful)
        # We can replay multiply() step-by-step for the trace if needed
        trace = []
        current = ILogicState.DIRECT
        for i, (name, state) in enumerate(agent_states):
            next_val = multiply(current, state)
            trace.append({
                "step": i + 1,
                "agent": name,
                "input": ILogicState.name(current),
                "op": ILogicState.name(state),
                "output": ILogicState.name(next_val)
            })
            current = next_val
            if current == ILogicState.ABSORBED:
                break

        result = {
            "route": route_str,
            "final_state": ILogicState.name(final_state),
            "terminated": final_state == ILogicState.ABSORBED,
            "steps_to_result": steps,
            "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 resolver using system algebra."""
        cond_upper = condition.strip().upper()
        if cond_upper not in self.states:
            return {"error": f"Unknown condition: {cond_upper}"}
        
        target = self.states[cond_upper]
        
        # Solving for X: target * X = DIRECT
        # In our group, X is the inverse.
        # We can iterate through valid states to find the one that resolves.
        valid_resolvers = []
        
        for name, state in self.states.items():
            if state == ILogicState.ABSORBED:
                 continue
            
            # Use system multiplication
            outcome = multiply(target, state)
            if outcome == ILogicState.DIRECT:
                valid_resolvers.append(name)

        result = {
            "condition": cond_upper,
            "resolvers": valid_resolvers,
            "primary_resolver": valid_resolvers[0] if valid_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 using system primitive."""
        state_names = [s.strip().upper() for s in states_str.split(",")]
        try:
           complex_states = [self.states[s] for s in state_names]
        except KeyError as e:
            return {"error": f"Unknown state: {e}"}

        # Delegate to system primitive
        is_cycle, description, period = detect_cycle(complex_states)
        
        result = {
            "states": state_names,
            "has_cycle": is_cycle,
            "description": description,
            "period": period
        }
        
        if as_json:
            return json.dumps(result, indent=2)
        return result

    def demo(self, as_json: bool = False) -> Dict[str, Any]:
        """Run demos using system primitives."""
        demos = [
            self.route("gateway:DIRECT,firewall:INVERSE,sink:ABSORBED"),
            self.resolve("INVERSE"),
            self.cycle("SPIRAL,SPIRAL,SPIRAL,SPIRAL")
        ]
        result = {"demos": demos}
        if as_json:
            return json.dumps(result, indent=2)
        return result

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        commands = {
            "status": lambda: self.status(as_json),
            "route": lambda: self.route(args[0], as_json) if args else {"error": "Args required"},
            "resolve": lambda: self.resolve(args[0], as_json) if args else {"error": "Args required"},
            "cycle": lambda: self.cycle(args[0], as_json) if args else {"error": "Args required"},
            "demo": lambda: self.demo(as_json)
        }
        return commands.get(command, lambda: {"error": f"Unknown command: {command}"})()

