"""Thin wrapper over openlineage-python.

Public surface: `emit_start(run_id, job_name, inputs, outputs)`
and `emit_complete(run_id, job_name, status)`. The backend URL is
read from the `OPENLINEAGE_URL` env var; if it begins with `file://`
events are appended to JSON files under `events/` (fallback for
machines without Docker / Marquez).
"""

from __future__ import annotations

import json
import os
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

try:
    from openlineage.client import OpenLineageClient
    from openlineage.client.event_v2 import Dataset, Job, Run, RunEvent, RunState
    _HAS_OL = True
except ImportError:  # fallback if package not installed
    _HAS_OL = False

PRODUCER = "https://github.com/dmitry-kharytonov/claude-lessons/data-engineering"
DEFAULT_NAMESPACE = "gharchive-pipeline"
EVENTS_DIR = Path(__file__).parent / "events"


def _file_backend_write(event: dict[str, Any]) -> None:
    EVENTS_DIR.mkdir(parents=True, exist_ok=True)
    out = EVENTS_DIR / f"{event['run']['runId']}-{event['eventType']}.json"
    out.write_text(json.dumps(event, indent=2, default=str))


def emit_start(
    run_id: str,
    job_name: str,
    inputs: list[str] | None = None,
    outputs: list[str] | None = None,
    namespace: str = DEFAULT_NAMESPACE,
) -> None:
    """Emit an OpenLineage START event for a job run."""
    backend = os.environ.get("OPENLINEAGE_URL", "file://./events")
    payload: dict[str, Any] = {
        "eventType": "START",
        "eventTime": datetime.now(timezone.utc).isoformat(),
        "producer": PRODUCER,
        "job": {"namespace": namespace, "name": job_name},
        "run": {"runId": run_id},
        "inputs": [{"namespace": namespace, "name": n} for n in inputs or []],
        "outputs": [{"namespace": namespace, "name": n} for n in outputs or []],
    }
    if backend.startswith("file://") or not _HAS_OL:
        _file_backend_write(payload)
        return
    client = OpenLineageClient(url=backend)
    client.emit(RunEvent(
        eventType=RunState.START,
        eventTime=payload["eventTime"],
        run=Run(runId=run_id),
        job=Job(namespace=namespace, name=job_name),
        inputs=[Dataset(namespace=namespace, name=n) for n in inputs or []],
        outputs=[Dataset(namespace=namespace, name=n) for n in outputs or []],
        producer=PRODUCER,
    ))


def emit_complete(
    run_id: str,
    job_name: str,
    status: str = "COMPLETE",
    namespace: str = DEFAULT_NAMESPACE,
) -> None:
    """Emit an OpenLineage COMPLETE (or FAIL) event for a job run."""
    backend = os.environ.get("OPENLINEAGE_URL", "file://./events")
    payload: dict[str, Any] = {
        "eventType": status,
        "eventTime": datetime.now(timezone.utc).isoformat(),
        "producer": PRODUCER,
        "job": {"namespace": namespace, "name": job_name},
        "run": {"runId": run_id},
    }
    if backend.startswith("file://") or not _HAS_OL:
        _file_backend_write(payload)
        return
    client = OpenLineageClient(url=backend)
    client.emit(RunEvent(
        eventType=RunState[status],
        eventTime=payload["eventTime"],
        run=Run(runId=run_id),
        job=Job(namespace=namespace, name=job_name),
        producer=PRODUCER,
    ))


def new_run_id() -> str:
    """Return a fresh UUID string suitable for use as an OpenLineage run ID."""
    return str(uuid.uuid4())
