"""
Example: Biological Taxonomy Domain

Demonstrates how to implement a BRS domain for tracking
taxonomic relationships and evolutionary patterns.
"""
from __future__ import annotations

import datetime
from pathlib import Path
from typing import List, Tuple

from ..core import Node, Edge, Evidence, Pattern, WorldBundle, TimeRange
from ..storage import CASStore
from ..inference import best_path_to_any_root, get_ancestors
from ..domains.registry import SmokeTestResult


# =============================================================================
# Domain Constants
# =============================================================================

DOMAIN_ID = "biology"

# Root taxa
TAXONOMY_ROOTS = [
    "BACTERIA",
    "ARCHAEA",
    "EUKARYA",
]

# Domain/Kingdom level roots for more specific queries
EUKARYOTIC_ROOTS = [
    "ANIMALIA",
    "PLANTAE",
    "FUNGI",
    "PROTISTA",
]


# =============================================================================
# Seed Data Functions
# =============================================================================

def create_biology_nodes() -> List[Node]:
    """Create sample taxonomy nodes."""
    return [
        # Domains
        Node(
            id="EUKARYA",
            name="Eukarya",
            node_type="domain",
            properties={"rank": "domain", "cell_type": "eukaryotic"},
            first_attested=TimeRange(-2000000000),
            regions=("Global",),
        ),
        # Kingdoms
        Node(
            id="ANIMALIA",
            name="Animalia",
            node_type="kingdom",
            properties={"rank": "kingdom", "nutrition": "heterotrophic"},
            first_attested=TimeRange(-600000000),
            regions=("Global",),
        ),
        Node(
            id="PLANTAE",
            name="Plantae",
            node_type="kingdom",
            properties={"rank": "kingdom", "nutrition": "autotrophic"},
            first_attested=TimeRange(-500000000),
            regions=("Global",),
        ),
        # Phyla
        Node(
            id="CHORDATA",
            name="Chordata",
            node_type="phylum",
            properties={"rank": "phylum", "defining_feature": "notochord"},
            first_attested=TimeRange(-530000000),
            regions=("Global",),
        ),
        # Classes
        Node(
            id="MAMMALIA",
            name="Mammalia",
            node_type="class",
            properties={"rank": "class", "defining_feature": "mammary_glands"},
            first_attested=TimeRange(-225000000),
            regions=("Global",),
        ),
        # Orders
        Node(
            id="PRIMATES",
            name="Primates",
            node_type="order",
            properties={"rank": "order", "defining_feature": "grasping_hands"},
            first_attested=TimeRange(-65000000),
            regions=("Global",),
        ),
        Node(
            id="CARNIVORA",
            name="Carnivora",
            node_type="order",
            properties={"rank": "order", "defining_feature": "carnassial_teeth"},
            first_attested=TimeRange(-60000000),
            regions=("Global",),
        ),
        # Families
        Node(
            id="HOMINIDAE",
            name="Hominidae",
            node_type="family",
            properties={"rank": "family", "common_name": "great_apes"},
            first_attested=TimeRange(-20000000),
            regions=("Africa", "Asia"),
        ),
        Node(
            id="FELIDAE",
            name="Felidae",
            node_type="family",
            properties={"rank": "family", "common_name": "cats"},
            first_attested=TimeRange(-25000000),
            regions=("Global",),
        ),
        # Genera
        Node(
            id="HOMO",
            name="Homo",
            node_type="genus",
            properties={"rank": "genus"},
            first_attested=TimeRange(-2800000),
            regions=("Africa",),
        ),
        Node(
            id="PANTHERA",
            name="Panthera",
            node_type="genus",
            properties={"rank": "genus", "common_name": "big_cats"},
            first_attested=TimeRange(-10000000),
            regions=("Global",),
        ),
        # Species
        Node(
            id="HOMO_SAPIENS",
            name="Homo sapiens",
            node_type="species",
            properties={"rank": "species", "common_name": "human"},
            first_attested=TimeRange(-300000),
            regions=("Global",),
        ),
        Node(
            id="PANTHERA_LEO",
            name="Panthera leo",
            node_type="species",
            properties={"rank": "species", "common_name": "lion"},
            first_attested=TimeRange(-1000000),
            regions=("Africa", "Asia"),
        ),
    ]


def create_biology_edges() -> List[Edge]:
    """Create taxonomic hierarchy edges."""
    return [
        # Domain -> Kingdom
        Edge(id="E_EUK_AN", parent_id="EUKARYA", child_id="ANIMALIA",
             relation="contains", tier=0, confidence=1.0),
        Edge(id="E_EUK_PL", parent_id="EUKARYA", child_id="PLANTAE",
             relation="contains", tier=0, confidence=1.0),
        # Kingdom -> Phylum
        Edge(id="E_AN_CH", parent_id="ANIMALIA", child_id="CHORDATA",
             relation="contains", tier=0, confidence=1.0),
        # Phylum -> Class
        Edge(id="E_CH_MA", parent_id="CHORDATA", child_id="MAMMALIA",
             relation="contains", tier=0, confidence=1.0),
        # Class -> Order
        Edge(id="E_MA_PR", parent_id="MAMMALIA", child_id="PRIMATES",
             relation="contains", tier=0, confidence=1.0),
        Edge(id="E_MA_CA", parent_id="MAMMALIA", child_id="CARNIVORA",
             relation="contains", tier=0, confidence=1.0),
        # Order -> Family
        Edge(id="E_PR_HO", parent_id="PRIMATES", child_id="HOMINIDAE",
             relation="contains", tier=0, confidence=1.0),
        Edge(id="E_CA_FE", parent_id="CARNIVORA", child_id="FELIDAE",
             relation="contains", tier=0, confidence=1.0),
        # Family -> Genus
        Edge(id="E_HO_HM", parent_id="HOMINIDAE", child_id="HOMO",
             relation="contains", tier=0, confidence=1.0),
        Edge(id="E_FE_PA", parent_id="FELIDAE", child_id="PANTHERA",
             relation="contains", tier=0, confidence=1.0),
        # Genus -> Species
        Edge(id="E_HM_HS", parent_id="HOMO", child_id="HOMO_SAPIENS",
             relation="contains", tier=0, confidence=1.0),
        Edge(id="E_PA_PL", parent_id="PANTHERA", child_id="PANTHERA_LEO",
             relation="contains", tier=0, confidence=1.0),
    ]


def create_biology_patterns() -> List[Pattern]:
    """Create biological patterns and rules."""
    return [
        Pattern(
            id="PAT_TAXONOMY_RANK",
            name="Taxonomic Rank Hierarchy",
            summary="Standard taxonomic ranks from Domain to Species",
            invariants=(
                "Domain > Kingdom > Phylum > Class > Order > Family > Genus > Species",
                "Each taxon belongs to exactly one parent at each higher rank",
            ),
            operators=("rank_assignment", "parent_inference"),
        ),
        Pattern(
            id="PAT_COMMON_DESCENT",
            name="Common Descent",
            summary="All organisms share common ancestors",
            invariants=(
                "Every species has at least one ancestral species",
                "Shared ancestors explain shared traits",
            ),
            operators=("ancestor_tracing", "trait_inheritance"),
        ),
        Pattern(
            id="PAT_SPECIATION",
            name="Speciation Mechanisms",
            summary="Processes by which new species arise",
            invariants=(
                "Geographic isolation can lead to speciation",
                "Reproductive isolation defines species boundaries",
            ),
            operators=("isolation_detection", "species_splitting"),
        ),
    ]


def seed_biology_domain(store: CASStore, world_label: str = "green") -> str:
    """
    Seed the biology domain with sample data.

    Args:
        store: Storage backend
        world_label: Version label for the world

    Returns:
        Hash of the created world
    """
    node_ids = []
    edge_ids = []
    pattern_ids = []

    # Store nodes
    for node in create_biology_nodes():
        h = store.put_object("Node", node)
        store.upsert_node(node.id, node.name, h)
        node_ids.append(node.id)

    # Store edges
    for edge in create_biology_edges():
        h = store.put_object("Edge", edge)
        store.upsert_edge(
            edge.id, edge.parent_id, edge.child_id,
            edge.relation, edge.tier, edge.confidence, h
        )
        edge_ids.append(edge.id)

    # Store patterns
    for pattern in create_biology_patterns():
        h = store.put_object("Pattern", pattern)
        pattern_ids.append(pattern.id)

    # Create world bundle
    world = WorldBundle(
        domain_id=DOMAIN_ID,
        version_label=world_label,
        node_ids=tuple(node_ids),
        edge_ids=tuple(edge_ids),
        evidence_ids=(),
        pattern_ids=tuple(pattern_ids),
        created_utc=datetime.datetime.utcnow().isoformat() + "Z",
        notes="Biology taxonomy domain - seeded",
    )

    return store.put_world(world)


# =============================================================================
# Smoke Tests
# =============================================================================

def run_smoke(store: CASStore, domain_id: str, world_label: str) -> SmokeTestResult:
    """
    Biology domain smoke tests.

    Verifies:
    1. Key taxonomic paths exist
    2. Rank hierarchy is respected
    """
    msgs: List[str] = []
    tests = 0
    fails = 0

    # Test that humans trace to Eukarya
    tests += 1
    try:
        result = best_path_to_any_root(store, "HOMO_SAPIENS", TAXONOMY_ROOTS)
        if result is None:
            fails += 1
            msgs.append("FAIL: Homo sapiens should connect to Eukarya")
        elif result.root != "EUKARYA":
            fails += 1
            msgs.append(f"FAIL: Homo sapiens should trace to EUKARYA, got {result.root}")
        else:
            msgs.append("OK: Homo sapiens -> Eukarya")
    except Exception as e:
        msgs.append(f"SKIP: {e}")

    # Test that lions trace to Animalia
    tests += 1
    try:
        result = best_path_to_any_root(store, "PANTHERA_LEO", EUKARYOTIC_ROOTS)
        if result is None:
            fails += 1
            msgs.append("FAIL: Panthera leo should connect to Animalia")
        elif result.root != "ANIMALIA":
            fails += 1
            msgs.append(f"FAIL: Panthera leo should trace to ANIMALIA, got {result.root}")
        else:
            msgs.append("OK: Panthera leo -> Animalia")
    except Exception as e:
        msgs.append(f"SKIP: {e}")

    # Verify path length (species -> domain should be 7 hops)
    tests += 1
    try:
        ancestors = get_ancestors(store, "HOMO_SAPIENS", max_depth=10)
        expected = {"HOMO", "HOMINIDAE", "PRIMATES", "MAMMALIA", "CHORDATA", "ANIMALIA", "EUKARYA"}
        missing = expected - ancestors
        if missing:
            fails += 1
            msgs.append(f"FAIL: Missing ancestors for Homo sapiens: {missing}")
        else:
            msgs.append("OK: Full taxonomic ancestry present")
    except Exception as e:
        msgs.append(f"SKIP: {e}")

    return (tests, fails, msgs)


# Register smoke tests
SMOKE_LEVELS = {
    "smoke": run_smoke,
    "regression": run_smoke,
    "deep": run_smoke,
}


# =============================================================================
# Usage Example
# =============================================================================

def example_usage():
    """Demonstrate domain usage."""
    from pathlib import Path
    import tempfile

    with tempfile.TemporaryDirectory() as tmpdir:
        store = CASStore(Path(tmpdir))

        # Seed the domain
        world_hash = seed_biology_domain(store)
        print(f"Created biology world: {world_hash[:12]}...")

        # Run smoke tests
        tests, fails, msgs = run_smoke(store, DOMAIN_ID, "green")
        print(f"\nSmoke tests: {tests} run, {fails} failed")
        for msg in msgs:
            print(f"  {msg}")

        # Query ancestors
        ancestors = get_ancestors(store, "HOMO_SAPIENS")
        print(f"\nHomo sapiens ancestors: {sorted(ancestors)}")

        store.close()


if __name__ == "__main__":
    example_usage()
