"""
Ecosystem Shell - Token Relationship and Connectivity Analysis
HEX_ID: EC0NSH01

Standalone version for cubesquare.

INPUTS:
    - Token hex IDs (8-char strings)
    - Basin numbers (0-130)
    - Category names (CORE, ACTION, DATA, RELATION, META)

OUTPUTS:
    - Connectivity reports (total_tokens, relationships, avg_degree, bridges)
    - Neighbor lists with relationship types
    - Bridge statistics between categories
    - Path finding between tokens
    - Validation status (PASSED, WARNING, FAILED)

COMMANDS:
    --status         Quick health status
    --report         Full connectivity report
    --validate       Run all validators
    --neighbors HEX  Get neighbors of a token
    --bridges        List bridge statistics
    --path S T       Find path between two hex IDs
    --category CAT   List tokens in category
    --basin NUM      List tokens in basin

Categories by basin range:
    CORE:     0-25   (Identity, structure)
    ACTION:   26-50  (Functions, methods)
    DATA:     51-75  (Models, schemas)
    RELATION: 76-100 (Connections, links)
    META:     101-130 (Config, tests, docs)
"""

from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional, Tuple, Any, Set
from collections import defaultdict
import json

# Import from cubesquare core
from .._core.constants import PRIME_131


class ValidationStatus(Enum):
    """Validation status codes."""
    PASSED = "PASSED"
    WARNING = "WARNING"
    FAILED = "FAILED"
    SKIPPED = "SKIPPED"


class RelationshipType(Enum):
    """Token relationship types."""
    BASIN_CLUSTER = "BASIN_CLUSTER"
    BASIN_ANCHOR = "BASIN_ANCHOR"
    LAYER_SIBLING = "LAYER_SIBLING"
    BRIDGE_CORE_ACTION = "BRIDGE_CORE_ACTION"
    BRIDGE_CORE_DATA = "BRIDGE_CORE_DATA"
    BRIDGE_CORE_RELATION = "BRIDGE_CORE_RELATION"
    BRIDGE_CORE_META = "BRIDGE_CORE_META"
    BRIDGE_ACTION_DATA = "BRIDGE_ACTION_DATA"
    BRIDGE_ACTION_RELATION = "BRIDGE_ACTION_RELATION"
    BRIDGE_ACTION_META = "BRIDGE_ACTION_META"
    BRIDGE_DATA_RELATION = "BRIDGE_DATA_RELATION"
    BRIDGE_DATA_META = "BRIDGE_DATA_META"
    BRIDGE_RELATION_META = "BRIDGE_RELATION_META"


class SemanticCategory(Enum):
    """Semantic categories with basin ranges."""
    CORE = (0, 25)
    ACTION = (26, 50)
    DATA = (51, 75)
    RELATION = (76, 100)
    META = (101, 130)

    @classmethod
    def from_basin(cls, basin: int) -> 'SemanticCategory':
        """Get category from basin number."""
        for cat in cls:
            low, high = cat.value
            if low <= basin <= high:
                return cat
        return cls.META


@dataclass
class ConnectivityReport:
    """Report on ecosystem connectivity."""
    total_tokens: int = 0
    total_relationships: int = 0
    connected_nodes: int = 0
    isolated_nodes: int = 0
    connectivity_ratio: float = 0.0
    avg_degree: float = 0.0
    bridges_count: int = 0
    relationship_distribution: Dict[str, int] = field(default_factory=dict)
    category_distribution: Dict[str, int] = field(default_factory=dict)

    def to_dict(self) -> Dict:
        return {
            'total_tokens': self.total_tokens,
            'total_relationships': self.total_relationships,
            'connected_nodes': self.connected_nodes,
            'isolated_nodes': self.isolated_nodes,
            'connectivity_ratio': self.connectivity_ratio,
            'avg_degree': round(self.avg_degree, 2),
            'bridges_count': self.bridges_count,
            'relationship_distribution': self.relationship_distribution,
            'category_distribution': self.category_distribution,
        }


@dataclass
class ValidationResult:
    """Single validation check result."""
    check_name: str
    status: ValidationStatus
    message: str
    details: Dict[str, Any] = field(default_factory=dict)


@dataclass
class HealthReport:
    """Overall health report."""
    overall_status: ValidationStatus
    validations: List[ValidationResult]
    recommendations: List[str]
    timestamp: float = 0.0

    def to_dict(self) -> Dict:
        return {
            'overall_status': self.overall_status.value,
            'validations': [
                {
                    'check_name': v.check_name,
                    'status': v.status.value,
                    'message': v.message,
                }
                for v in self.validations
            ],
            'recommendations': self.recommendations,
        }


class EcosystemShell:
    """
    Ecosystem Connectivity Shell

    Provides analysis of token relationships and connectivity.
    Standalone version without database dependencies.

    Usage:
        eco = EcosystemShell()
        eco.add_token("2A9E0000", basin=19, layer=2)
        eco.add_relationship("2A9E0000", "2A9E0100", RelationshipType.LAYER_SIBLING)
        report = eco.get_connectivity_report()
    """

    def __init__(self):
        # Token storage
        self.tokens: Dict[str, Dict] = {}
        # Relationships: (source, target) -> type
        self.relationships: Dict[Tuple[str, str], RelationshipType] = {}
        # Adjacency list for efficient neighbor lookup
        self._adjacency: Dict[str, Set[Tuple[str, str]]] = defaultdict(set)  # hex -> {(neighbor, type)}

    def add_token(self, hex_id: str, basin: int = 0, layer: int = 1, **metadata) -> None:
        """Add a token to the ecosystem."""
        category = SemanticCategory.from_basin(basin)
        self.tokens[hex_id] = {
            'hex_id': hex_id,
            'basin': basin,
            'layer': layer,
            'category': category.name,
            **metadata
        }

    def add_relationship(self, source: str, target: str, rel_type: RelationshipType) -> None:
        """Add a relationship between two tokens."""
        self.relationships[(source, target)] = rel_type
        self._adjacency[source].add((target, rel_type.value))
        self._adjacency[target].add((source, rel_type.value))

    def get_neighbors(self, hex_id: str) -> List[Tuple[str, str]]:
        """Get neighbors of a token with relationship types."""
        return list(self._adjacency.get(hex_id, set()))

    def get_token_degree(self, hex_id: str) -> int:
        """Get connection count for a token."""
        return len(self._adjacency.get(hex_id, set()))

    def get_basin_cluster(self, basin: int) -> List[str]:
        """Get all tokens in a basin."""
        return [h for h, t in self.tokens.items() if t.get('basin') == basin]

    def get_layer_siblings(self, layer: int) -> List[str]:
        """Get all tokens in a layer."""
        return [h for h, t in self.tokens.items() if t.get('layer') == layer]

    def get_category_tokens(self, category: SemanticCategory) -> List[str]:
        """Get all tokens in a category."""
        return [h for h, t in self.tokens.items() if t.get('category') == category.name]

    def find_path(self, source: str, target: str, max_hops: int = 10) -> Optional[List[str]]:
        """Find shortest path between two tokens (BFS)."""
        if source not in self.tokens or target not in self.tokens:
            return None

        if source == target:
            return [source]

        visited = {source}
        queue = [(source, [source])]

        while queue:
            current, path = queue.pop(0)
            if len(path) > max_hops:
                continue

            for neighbor, _ in self._adjacency.get(current, set()):
                if neighbor == target:
                    return path + [neighbor]
                if neighbor not in visited:
                    visited.add(neighbor)
                    queue.append((neighbor, path + [neighbor]))

        return None

    def get_bridges(self) -> List[Tuple[str, str]]:
        """Get cross-category bridge relationships."""
        bridges = []
        for (source, target), rel_type in self.relationships.items():
            if rel_type.value.startswith('BRIDGE_'):
                bridges.append((source, target))
        return bridges

    def get_relationship_stats(self) -> Dict[str, int]:
        """Get relationship count by type."""
        stats = defaultdict(int)
        for rel_type in self.relationships.values():
            stats[rel_type.value] += 1
        return dict(stats)

    def get_connectivity_report(self) -> ConnectivityReport:
        """Generate full connectivity report."""
        total_tokens = len(self.tokens)
        total_relationships = len(self.relationships)

        # Count connected nodes
        connected = set()
        for source, target in self.relationships.keys():
            connected.add(source)
            connected.add(target)
        connected_nodes = len(connected)
        isolated_nodes = total_tokens - connected_nodes

        # Average degree
        if total_tokens > 0:
            total_degree = sum(self.get_token_degree(h) for h in self.tokens)
            avg_degree = total_degree / total_tokens
        else:
            avg_degree = 0.0

        # Relationship distribution
        rel_dist = self.get_relationship_stats()

        # Bridge count
        bridges_count = sum(1 for t in rel_dist.keys() if t.startswith('BRIDGE_'))

        # Category distribution
        cat_dist = defaultdict(int)
        for t in self.tokens.values():
            cat_dist[t.get('category', 'UNKNOWN')] += 1

        return ConnectivityReport(
            total_tokens=total_tokens,
            total_relationships=total_relationships,
            connected_nodes=connected_nodes,
            isolated_nodes=isolated_nodes,
            connectivity_ratio=connected_nodes / max(total_tokens, 1),
            avg_degree=avg_degree,
            bridges_count=bridges_count,
            relationship_distribution=rel_dist,
            category_distribution=dict(cat_dist),
        )

    def validate(self) -> HealthReport:
        """Run all validation checks."""
        validations = []
        recommendations = []

        # Check 1: Token count
        token_count = len(self.tokens)
        if token_count == 0:
            validations.append(ValidationResult(
                check_name="token_count",
                status=ValidationStatus.WARNING,
                message="No tokens in ecosystem"
            ))
            recommendations.append("Add tokens to the ecosystem")
        else:
            validations.append(ValidationResult(
                check_name="token_count",
                status=ValidationStatus.PASSED,
                message=f"{token_count} tokens indexed"
            ))

        # Check 2: Connectivity
        report = self.get_connectivity_report()
        if report.isolated_nodes > 0:
            validations.append(ValidationResult(
                check_name="connectivity",
                status=ValidationStatus.WARNING,
                message=f"{report.isolated_nodes} isolated nodes"
            ))
            recommendations.append("Connect isolated nodes to ecosystem")
        else:
            validations.append(ValidationResult(
                check_name="connectivity",
                status=ValidationStatus.PASSED,
                message="100% connectivity"
            ))

        # Check 3: Cross-category bridges
        bridges = self.get_bridges()
        if len(bridges) == 0 and token_count > 1:
            validations.append(ValidationResult(
                check_name="bridges",
                status=ValidationStatus.WARNING,
                message="No cross-category bridges"
            ))
            recommendations.append("Add bridge relationships between categories")
        else:
            validations.append(ValidationResult(
                check_name="bridges",
                status=ValidationStatus.PASSED,
                message=f"{len(bridges)} cross-category bridges"
            ))

        # Overall status
        has_failure = any(v.status == ValidationStatus.FAILED for v in validations)
        has_warning = any(v.status == ValidationStatus.WARNING for v in validations)
        if has_failure:
            overall = ValidationStatus.FAILED
        elif has_warning:
            overall = ValidationStatus.WARNING
        else:
            overall = ValidationStatus.PASSED

        return HealthReport(
            overall_status=overall,
            validations=validations,
            recommendations=recommendations,
        )

    def get_status(self, json_output: bool = False) -> Dict:
        """Get ecosystem status."""
        report = self.get_connectivity_report()
        health = self.validate()

        status = {
            'hex_id': 'EC0NSH01',
            'total_tokens': report.total_tokens,
            'total_relationships': report.total_relationships,
            'connected_nodes': report.connected_nodes,
            'isolated_nodes': report.isolated_nodes,
            'connectivity_ratio': report.connectivity_ratio,
            'avg_degree': report.avg_degree,
            'bridges_count': report.bridges_count,
            'health_status': health.overall_status.value,
        }

        if json_output:
            return json.dumps(status, indent=2)
        return status

    def run(self, command: str, *args, as_json: bool = False) -> Any:
        """Run a shell command (CLI interface)."""
        commands = {
            'status': lambda: self.get_status(),
            'report': lambda: self.get_connectivity_report().to_dict(),
            'validate': lambda: self.validate().to_dict(),
            'neighbors': lambda: self.get_neighbors(args[0] if args else ""),
            'bridges': lambda: self.get_bridges(),
            'path': lambda: self.find_path(args[0] if len(args) > 0 else "",
                                            args[1] if len(args) > 1 else ""),
            'category': lambda: self.get_category_tokens(
                SemanticCategory[args[0].upper()] if args else SemanticCategory.CORE),
            'basin': lambda: self.get_basin_cluster(int(args[0]) if args else 0),
        }
        if command in commands:
            result = commands[command]()
            if as_json and isinstance(result, dict):
                return json.dumps(result, indent=2, default=str)
            return result
        return {'error': f'Unknown command: {command}', 'available': list(commands.keys())}

    def close(self):
        """Cleanup (compatibility method)."""
        pass


# Convenience functions
def get_token_neighbors(hex_id: str) -> List[Tuple[str, str]]:
    """Get neighbors of a token (requires active shell)."""
    # This is a standalone stub - in production would use database
    return []


def get_basin_cluster(basin: int) -> List[str]:
    """Get tokens in a basin (requires active shell)."""
    return []


def get_layer_siblings(layer: int) -> List[str]:
    """Get tokens in a layer (requires active shell)."""
    return []


def find_bridge_path(source: str, target: str) -> Optional[List[str]]:
    """Find path between tokens (requires active shell)."""
    return None


def get_category_tokens(category: SemanticCategory) -> List[str]:
    """Get tokens in category (requires active shell)."""
    return []


def get_token_degree(hex_id: str) -> int:
    """Get token degree (requires active shell)."""
    return 0


def generate_health_report() -> HealthReport:
    """Generate health report (requires active shell)."""
    return HealthReport(
        overall_status=ValidationStatus.SKIPPED,
        validations=[],
        recommendations=["Initialize EcosystemShell for full analysis"],
    )


def quick_check() -> Tuple[ValidationStatus, str]:
    """Quick health check (requires active shell)."""
    return ValidationStatus.SKIPPED, "Standalone mode - use EcosystemShell instance"
