from __future__ import annotations

from datetime import datetime

from agentbyte.workflow.core.models import Edge
from agentbyte.workflow.core.models import EdgeActivatedEvent
from agentbyte.workflow.core.models import EdgeCondition
from agentbyte.workflow.core.models import StepCompletedEvent
from agentbyte.workflow.core.models import StepExecution
from agentbyte.workflow.core.models import StepFailedEvent
from agentbyte.workflow.core.models import StepMetadata
from agentbyte.workflow.core.models import StepProgressEvent
from agentbyte.workflow.core.models import StepStartedEvent
from agentbyte.workflow.core.models import StepStatus
from agentbyte.workflow.core.models import WorkflowCompletedEvent
from agentbyte.workflow.core.models import WorkflowErrorDetails
from agentbyte.workflow.core.models import WorkflowEventOrigin
from agentbyte.workflow.core.models import WorkflowEventType
from agentbyte.workflow.core.models import WorkflowExecution
from agentbyte.workflow.core.models import WorkflowFailedEvent
from agentbyte.workflow.core.models import WorkflowMetadata
from agentbyte.workflow.core.models import WorkflowStartedEvent
from agentbyte.workflow.core.models import WorkflowStatus
from agentbyte.workflow.core.models import WorkflowContext
from agentbyte.workflow.core.models import WorkflowValidationResult


def test_step_status_values() -> None:
    assert StepStatus.PENDING.value == "pending"
    assert StepStatus.RUNNING.value == "running"
    assert StepStatus.COMPLETED.value == "completed"
    assert StepStatus.FAILED.value == "failed"
    assert StepStatus.SKIPPED.value == "skipped"
    assert StepStatus.CANCELLED.value == "cancelled"


def test_workflow_status_values() -> None:
    assert WorkflowStatus.CREATED.value == "created"
    assert WorkflowStatus.RUNNING.value == "running"
    assert WorkflowStatus.COMPLETED.value == "completed"
    assert WorkflowStatus.FAILED.value == "failed"
    assert WorkflowStatus.CANCELLED.value == "cancelled"


def test_edge_defaults_and_condition() -> None:
    edge = Edge(from_step="a", to_step="b")

    assert edge.from_step == "a"
    assert edge.to_step == "b"
    assert edge.condition.type == "always"

    conditional = EdgeCondition(
        type="output_based",
        field="is_valid",
        operator="==",
        value=True,
    )
    assert conditional.type == "output_based"
    assert conditional.field == "is_valid"
    assert conditional.operator == "=="
    assert conditional.value is True


def test_context_from_state_ref_is_shared() -> None:
    shared_state = {"count": 1}
    context = WorkflowContext.from_state_ref(shared_state)

    context.set("count", 2)

    assert shared_state["count"] == 2
    assert context.get("count") == 2


def test_context_emit_progress_calls_callback() -> None:
    emitted: list[dict[str, object]] = []

    context = WorkflowContext(state={})
    context._progress_callback = lambda payload: emitted.append(payload)

    context.emit_progress("processing", completed=2, total=4, metadata={"phase": 1})

    assert len(emitted) == 1
    assert emitted[0]["message"] == "processing"
    assert emitted[0]["completed"] == 2
    assert emitted[0]["total"] == 4
    assert emitted[0]["metadata"] == {"phase": 1}


def test_context_to_dict_contains_workflow_state() -> None:
    context = WorkflowContext(state={"foo": "bar"})

    data = context.to_dict()

    assert data["workflow_state"] == {"foo": "bar"}
    assert list(data.keys()) == ["workflow_state"]


def test_workflow_execution_defaults() -> None:
    execution = WorkflowExecution(workflow_id="wf-1")

    assert execution.status is WorkflowStatus.CREATED
    assert execution.step_executions == {}
    assert execution.state == {}
    assert execution.id


def test_step_execution_defaults() -> None:
    step_execution = StepExecution(step_id="step-1")

    assert step_execution.status is StepStatus.PENDING
    assert step_execution.retry_count == 0
    assert step_execution.input_data is None
    assert step_execution.output_data is None


def test_workflow_metadata_defaults() -> None:
    metadata = WorkflowMetadata(name="Data Pipeline")

    assert metadata.name == "Data Pipeline"
    assert metadata.version == "1.0.0"
    assert metadata.tags == []
    assert isinstance(metadata.created_at, datetime)


def test_step_metadata_defaults() -> None:
    metadata = StepMetadata(name="validate")

    assert metadata.name == "validate"
    assert metadata.max_retries == 0
    assert metadata.timeout_seconds is None
    assert metadata.tags == []


def test_workflow_validation_result_defaults() -> None:
    result = WorkflowValidationResult(is_valid=True)

    assert result.is_valid is True
    assert result.errors == []
    assert result.warnings == []
    assert result.has_cycles is False
    assert result.unreachable_steps == []


def test_workflow_started_event_string() -> None:
    event = WorkflowStartedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 0),
        initial_input={"value": 42},
    )

    assert event.event_type is WorkflowEventType.WORKFLOW_STARTED
    assert event.origin is WorkflowEventOrigin.FRAMEWORK
    assert "Workflow started" in str(event)


def test_workflow_completed_event_string_with_duration() -> None:
    execution = WorkflowExecution(
        workflow_id="wf-1",
        status=WorkflowStatus.COMPLETED,
        start_time=datetime(2026, 1, 1, 10, 0, 0),
        end_time=datetime(2026, 1, 1, 10, 0, 2),
        step_executions={"a": StepExecution(step_id="a", status=StepStatus.COMPLETED)},
    )
    event = WorkflowCompletedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 2),
        execution=execution,
    )

    rendered = str(event)
    assert event.event_type is WorkflowEventType.WORKFLOW_COMPLETED
    assert "Workflow completed in 2.00s" in rendered
    assert "(1 steps)" in rendered


def test_workflow_completed_event_serializes_final_output() -> None:
    execution = WorkflowExecution(
        workflow_id="wf-1",
        status=WorkflowStatus.COMPLETED,
        step_executions={"a": StepExecution(step_id="a", status=StepStatus.COMPLETED)},
    )
    event = WorkflowCompletedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 2),
        execution=execution,
        final_output={"response": "done", "usage": {"tokens_output": 12}},
    )

    dumped = event.model_dump(mode="json")

    assert dumped["final_output"] == {
        "response": "done",
        "usage": {"tokens_output": 12},
    }


def test_step_events_strings() -> None:
    started = StepStartedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 0),
        step_id="step-a",
        input_data={"x": 1},
    )
    completed = StepCompletedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 1),
        step_id="step-a",
        output_data={"y": 2},
        duration_seconds=1.0,
    )
    progress = StepProgressEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 0),
        step_id="step-a",
        message="halfway",
        completed=1,
        total=2,
    )
    edge = EdgeActivatedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 1),
        from_step="step-a",
        to_step="step-b",
        data={"y": 2},
    )

    assert "Step 'step-a' started" in str(started)
    assert started.origin is WorkflowEventOrigin.FRAMEWORK
    assert "Step 'step-a' completed" in str(completed)
    assert progress.origin is WorkflowEventOrigin.STEP
    assert "halfway" in str(progress)
    assert "(1/2, 50%)" in str(progress)
    assert "step-a → step-b" in str(edge)


def test_failed_events_expose_structured_error_details() -> None:
    details = WorkflowErrorDetails(
        error_type="RuntimeError",
        message="boom",
        traceback="traceback...",
        workflow_id="wf-1",
        step_id="step-a",
        execution_id="exec-1",
    )
    workflow_event = WorkflowFailedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 1),
        error_details=details,
    )
    step_event = StepFailedEvent(
        workflow_id="wf-1",
        timestamp=datetime(2026, 1, 1, 10, 0, 1),
        step_id="step-a",
        error_details=details,
        duration_seconds=0.5,
    )

    assert workflow_event.error == "boom"
    assert workflow_event.error_details.error_type == "RuntimeError"
    assert step_event.error == "boom"
    assert step_event.origin is WorkflowEventOrigin.FRAMEWORK

    payload = step_event.model_dump(mode="json")
    assert payload["origin"] == WorkflowEventOrigin.FRAMEWORK.value
    assert payload["error_details"]["message"] == "boom"
