"""DAG (Directed Acyclic Graph) Execution Example.

This example demonstrates how to use Oclawma's DAG execution capabilities
for complex workflows with dependencies.
"""

import time

from oclawma.dag import DAG, DAGExecutor, DAGJob, execute_dag


def example_simple_dag():
    """Example 1: Simple linear workflow."""
    print("=" * 50)
    print("Example 1: Simple Linear Workflow")
    print("=" * 50)

    # Create a DAG for a data processing pipeline
    dag = DAG(dag_id="data-pipeline")

    # Add jobs with dependencies
    dag.add_job("fetch-data", payload={"source": "api"})
    dag.add_job("clean-data", payload={"operation": "clean"}, depends_on=["fetch-data"])
    dag.add_job("analyze-data", payload={"operation": "analyze"}, depends_on=["clean-data"])
    dag.add_job("generate-report", payload={"operation": "report"}, depends_on=["analyze-data"])

    # Define the job handler
    def handler(job: DAGJob):
        print(f"  Executing: {job.id}")
        time.sleep(0.1)  # Simulate work
        return f"{job.id}-result"

    # Execute the DAG
    result = execute_dag(dag, handler, max_workers=2)

    print(f"\nStatus: {result.status.value}")
    print(f"Completed: {result.progress.completed_jobs}/{result.progress.total_jobs}")
    print()


def example_parallel_execution():
    """Example 2: Parallel job execution."""
    print("=" * 50)
    print("Example 2: Parallel Job Execution")
    print("=" * 50)

    dag = DAG(dag_id="parallel-build")

    # Independent jobs (Level 0)
    dag.add_job("build-frontend")
    dag.add_job("build-backend")
    dag.add_job("build-docs")

    # Depends on multiple jobs (Level 1)
    dag.add_job(
        "run-tests",
        depends_on=["build-frontend", "build-backend"],
    )

    # Final step (Level 2)
    dag.add_job(
        "deploy",
        depends_on=["run-tests", "build-docs"],
    )

    def handler(job: DAGJob):
        print(f"  Executing: {job.id}")
        time.sleep(0.1)
        return f"{job.id}-done"

    result = execute_dag(dag, handler, max_workers=4)

    print(f"\nStatus: {result.status.value}")
    print(f"Execution order: {list(result.job_results.keys())}")
    print()


def example_error_handling():
    """Example 3: Error handling and retries."""
    print("=" * 50)
    print("Example 3: Error Handling and Retries")
    print("=" * 50)

    dag = DAG(dag_id="retry-example")

    # This job will fail twice then succeed (with max_retries=2)
    dag.add_job("flaky-job", max_retries=2)
    dag.add_job("dependent-job", depends_on=["flaky-job"])

    attempt_count = 0

    def handler(job: DAGJob):
        nonlocal attempt_count
        attempt_count += 1

        if job.id == "flaky-job" and attempt_count <= 2:
            print(f"  {job.id}: Attempt {attempt_count} failed!")
            raise ValueError(f"Simulated failure #{attempt_count}")

        print(f"  {job.id}: Success!")
        return f"{job.id}-result"

    result = execute_dag(dag, handler, max_workers=1)

    print(f"\nStatus: {result.status.value}")
    print(f"Total attempts: {attempt_count}")
    print(f"Flaky job retries: {result.job_results['flaky-job'].retry_count}")
    print()


def example_progress_tracking():
    """Example 4: Progress tracking with callbacks."""
    print("=" * 50)
    print("Example 4: Progress Tracking")
    print("=" * 50)

    dag = DAG(dag_id="progress-demo")

    # Create a DAG with multiple levels
    for i in range(3):
        dag.add_job(f"job-{i}")

    dag.add_job("level-1-a", depends_on=["job-0"])
    dag.add_job("level-1-b", depends_on=["job-1"])
    dag.add_job("level-1-c", depends_on=["job-2"])

    dag.add_job("final", depends_on=["level-1-a", "level-1-b", "level-1-c"])

    def on_progress(progress):
        print(
            f"  Progress: {progress.percent_complete:.0f}% "
            f"(Completed: {progress.completed_jobs}, "
            f"Running: {progress.running_jobs}, "
            f"Pending: {progress.pending_jobs})"
        )

    def handler(job: DAGJob):
        time.sleep(0.05)
        return job.id

    executor = DAGExecutor(max_workers=3)
    executor.on_progress(on_progress)

    result = executor.execute_dag(dag, handler)

    print(f"\nFinal Status: {result.status.value}")
    print(f"Duration: {result.progress.duration_seconds:.2f}s")
    print()


def example_topological_sort():
    """Example 5: Viewing topological sort levels."""
    print("=" * 50)
    print("Example 5: Topological Sort Levels")
    print("=" * 50)

    dag = DAG(dag_id="topology-demo")

    # Build a complex dependency graph
    dag.add_job("database")
    dag.add_job("cache")

    dag.add_job("auth-service", depends_on=["database"])
    dag.add_job("user-service", depends_on=["database"])
    dag.add_job("product-service", depends_on=["database", "cache"])

    dag.add_job("api-gateway", depends_on=["auth-service", "user-service", "product-service"])
    dag.add_job("web-frontend", depends_on=["api-gateway"])
    dag.add_job("mobile-app", depends_on=["api-gateway"])

    levels = dag.topological_sort()

    print("Execution levels (jobs in same level can run in parallel):")
    for i, level in enumerate(levels):
        print(f"  Level {i}: {', '.join(level)}")
    print()


def example_dag_validation():
    """Example 6: DAG validation and cycle detection."""
    print("=" * 50)
    print("Example 6: DAG Validation (Cycle Detection)")
    print("=" * 50)

    # Valid DAG
    valid_dag = DAG(dag_id="valid")
    valid_dag.add_job("a")
    valid_dag.add_job("b", depends_on=["a"])
    valid_dag.add_job("c", depends_on=["b"])

    try:
        valid_dag.validate()
        print("  Valid DAG: ✓ No cycles detected")
    except Exception as e:
        print(f"  Valid DAG: ✗ Unexpected error: {e}")

    # Invalid DAG with cycle
    cyclic_dag = DAG(dag_id="cyclic")
    cyclic_dag.add_job("a")
    cyclic_dag.add_job("b")
    cyclic_dag.add_job("c")
    cyclic_dag.add_dependency("b", "a")
    cyclic_dag.add_dependency("c", "b")
    cyclic_dag.add_dependency("a", "c")  # Creates cycle: a -> b -> c -> a

    try:
        cyclic_dag.validate()
        print("  Cyclic DAG: ✗ Cycle not detected!")
    except Exception as e:
        print(f"  Cyclic DAG: ✓ Correctly detected: {type(e).__name__}")

    print()


if __name__ == "__main__":
    print("\n")
    print("*" * 50)
    print("OCLAWMA DAG Execution Examples")
    print("*" * 50)
    print()

    example_simple_dag()
    example_parallel_execution()
    example_error_handling()
    example_progress_tracking()
    example_topological_sort()
    example_dag_validation()

    print("*" * 50)
    print("All examples completed!")
    print("*" * 50)
