"""E2E integration test for neptune-exporter -> local GoodSeed import."""

from __future__ import annotations

import os
import shutil
import subprocess
from pathlib import Path

import pytest
from goodseed.storage import read_configs, read_metrics, read_string_series


REPO_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_LOCAL_EXPORTER = REPO_ROOT.parent / "archive" / "neptune-exporter"
ENABLE_E2E_ENV = "GOODSEED_RUN_NEPTUNE_EXPORTER_E2E"
EXPORTER_DIR_ENV = "NEPTUNE_EXPORTER_DIR"


def _run(
    args: list[str],
    *,
    cwd: Path | None = None,
    env: dict[str, str] | None = None,
) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        args,
        cwd=str(cwd) if cwd else None,
        env=env,
        text=True,
        capture_output=True,
        check=False,
    )


def _clean_env(extra: dict[str, str] | None = None) -> dict[str, str]:
    env = os.environ.copy()
    # Avoid uv warning/noise when pytest itself runs inside another virtualenv.
    env.pop("VIRTUAL_ENV", None)
    if extra:
        env.update(extra)
    return env


def _resolve_exporter_dir(tmp_path: Path) -> Path:
    configured = os.environ.get(EXPORTER_DIR_ENV)
    if configured:
        exporter_dir = Path(configured).expanduser().resolve()
        if not exporter_dir.exists():
            raise AssertionError(
                f"{EXPORTER_DIR_ENV} is set but path does not exist: {exporter_dir}"
            )
        return exporter_dir

    if DEFAULT_LOCAL_EXPORTER.exists():
        return DEFAULT_LOCAL_EXPORTER

    raise AssertionError(
        "No neptune-exporter checkout found. "
        f"Set {EXPORTER_DIR_ENV} or provide {DEFAULT_LOCAL_EXPORTER}."
    )


def _run_uv_checked(args: list[str], *, cwd: Path, env: dict[str, str] | None = None) -> None:
    result = _run(args, cwd=cwd, env=_clean_env(env))
    if result.returncode != 0:
        raise AssertionError(
            "uv command failed.\n"
            f"command: {' '.join(args)}\n"
            f"stdout:\n{result.stdout}\n"
            f"stderr:\n{result.stderr}"
        )


def _create_fixture_parquet(exporter_dir: Path, tmp_path: Path) -> Path:
    data_path = tmp_path / "exports" / "data"
    script = """
from datetime import datetime, timezone
from decimal import Decimal
from pathlib import Path

import pyarrow.parquet as pq
from neptune_exporter import model
from neptune_exporter.utils import sanitize_path_part

base = Path(__DATA_PATH__)
project_id = "org/proj"
run_id = "RUN-1"

project_dir = base / sanitize_path_part(project_id)
project_dir.mkdir(parents=True, exist_ok=True)
out_file = project_dir / f"{sanitize_path_part(run_id)}_part_0.parquet"

rows = [
    {
        "project_id": project_id,
        "run_id": run_id,
        "attribute_path": "sys/name",
        "attribute_type": "string",
        "step": None,
        "timestamp": datetime(2025, 1, 1, tzinfo=timezone.utc),
        "int_value": None,
        "float_value": None,
        "string_value": "Imported experiment",
        "bool_value": None,
        "datetime_value": None,
        "string_set_value": None,
        "file_value": None,
        "histogram_value": None,
    },
    {
        "project_id": project_id,
        "run_id": run_id,
        "attribute_path": "sys/creation_time",
        "attribute_type": "datetime",
        "step": None,
        "timestamp": datetime(2025, 1, 1, tzinfo=timezone.utc),
        "int_value": None,
        "float_value": None,
        "string_value": None,
        "bool_value": None,
        "datetime_value": datetime(2025, 1, 1, tzinfo=timezone.utc),
        "string_set_value": None,
        "file_value": None,
        "histogram_value": None,
    },
    {
        "project_id": project_id,
        "run_id": run_id,
        "attribute_path": "train/loss",
        "attribute_type": "float_series",
        "step": Decimal("1.0"),
        "timestamp": datetime(2025, 1, 1, tzinfo=timezone.utc),
        "int_value": None,
        "float_value": 0.42,
        "string_value": None,
        "bool_value": None,
        "datetime_value": None,
        "string_set_value": None,
        "file_value": None,
        "histogram_value": None,
    },
    {
        "project_id": project_id,
        "run_id": run_id,
        "attribute_path": "train/logs",
        "attribute_type": "string_series",
        "step": Decimal("1.0"),
        "timestamp": datetime(2025, 1, 1, tzinfo=timezone.utc),
        "int_value": None,
        "float_value": None,
        "string_value": "hello",
        "bool_value": None,
        "datetime_value": None,
        "string_set_value": None,
        "file_value": None,
        "histogram_value": None,
    },
]

table = model.pa.Table.from_pylist(rows, schema=model.SCHEMA)
pq.write_table(table, out_file)
print(out_file)
""".replace("__DATA_PATH__", repr(str(data_path)))
    result = _run(["uv", "run", "python", "-c", script], cwd=exporter_dir)
    if result.returncode != 0:
        raise AssertionError(
            "Fixture parquet generation failed.\n"
            f"stdout:\n{result.stdout}\n"
            f"stderr:\n{result.stderr}"
        )
    return data_path


@pytest.mark.integration
def test_neptune_exporter_goodseed_loader_imports_into_local_goodseed(tmp_path):
    """Run neptune-exporter via uv and verify data lands in local GoodSeed storage."""
    if os.environ.get(ENABLE_E2E_ENV) != "1":
        pytest.skip(f"Set {ENABLE_E2E_ENV}=1 to run neptune-exporter E2E test.")
    if shutil.which("uv") is None:
        pytest.skip("uv is required for this E2E test.")

    exporter_dir = _resolve_exporter_dir(tmp_path)
    _run_uv_checked(["uv", "sync", "--extra", "goodseed"], cwd=exporter_dir)
    data_path = _create_fixture_parquet(exporter_dir, tmp_path)

    files_path = tmp_path / "exports" / "files"
    files_path.mkdir(parents=True, exist_ok=True)
    goodseed_home = tmp_path / "goodseed-home"
    log_file_base = tmp_path / "neptune_exporter_test.log"

    result = _run(
        [
            "uv",
            "run",
            "--with-editable",
            str(REPO_ROOT),
            "neptune-exporter",
            "load",
            "--loader",
            "goodseed",
            "--data-path",
            str(data_path),
            "--files-path",
            str(files_path),
            "--log-file",
            str(log_file_base),
            "--no-progress",
        ],
        cwd=exporter_dir,
        env=_clean_env({"GOODSEED_STORAGE": "local", "GOODSEED_HOME": str(goodseed_home)}),
    )
    log_output = "".join(
        path.read_text(encoding="utf-8", errors="replace")
        for path in sorted(tmp_path.glob("neptune_exporter_test*.log"))
    )
    assert result.returncode == 0, (
        "Exporter load command failed.\n"
        f"stdout:\n{result.stdout}\n"
        f"stderr:\n{result.stderr}\n"
        f"log_output:\n{log_output}"
    )

    db_path = goodseed_home / "projects" / "org" / "proj" / "runs" / "RUN-1.sqlite"
    assert db_path.exists(), (
        "Expected imported GoodSeed run database was not created.\n"
        f"expected_db_path: {db_path}\n"
        f"stdout:\n{result.stdout}\n"
        f"stderr:\n{result.stderr}\n"
        f"log_output:\n{log_output}"
    )

    configs = read_configs(db_path)
    assert configs["neptune/project_id"] == "org/proj"
    assert configs["neptune/run_id"] == "RUN-1"
    assert configs["sys/name"] == "Imported experiment"

    metrics = read_metrics(db_path, "train/loss")
    assert len(metrics) == 1
    assert metrics[0]["step"] == 1
    assert metrics[0]["value"] == 0.42

    strings = read_string_series(db_path, "train/logs")
    assert strings["total"] == 1
    assert strings["points"][0]["step"] == 1
    assert strings["points"][0]["value"] == "hello"
