from __future__ import annotations

import os
import subprocess
from datetime import datetime, timezone
from pathlib import Path

import pytest

from osm_lead_source_service.errors import PbfDownloadRejected
from osm_lead_source_service.markets import GeoBounds
from osm_lead_source_service.pbf.extract import extract_country_pbf
from osm_lead_source_service.pbf.geofabrik import AcquiredPbf


def _acquisition(tmp_path: Path) -> AcquiredPbf:
    source = tmp_path / "source.osm.pbf"
    source.write_bytes(b"source")
    now = datetime(2026, 7, 20, tzinfo=timezone.utc)
    return AcquiredPbf(
        "source-central-america",
        "https://download.geofabrik.de/central-america-latest.osm.pbf",
        source,
        "a" * 64,
        "b" * 32,
        source.stat().st_size,
        now,
        now,
        False,
    )


def _binary(tmp_path: Path) -> Path:
    binary = tmp_path / "osmium"
    binary.write_text("#!/bin/sh\n")
    binary.chmod(0o700)
    return binary


def test_extracts_and_reuses_country_cache(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    calls: list[list[str]] = []

    def fake_run(args: list[str], **_kwargs: object) -> subprocess.CompletedProcess[str]:
        calls.append(args)
        output = Path(
            next(value.removeprefix("--output=") for value in args if value.startswith("--output="))
        )
        output.write_bytes(b"country")
        return subprocess.CompletedProcess(args, 0, "", "")

    monkeypatch.setattr(subprocess, "run", fake_run)
    bounds = GeoBounds(-620_000_000, 160_000_000, -610_000_000, 180_000_000)
    first = extract_country_pbf(
        _acquisition(tmp_path),
        bounds,
        tmp_path / "cache",
        osmium_binary=_binary(tmp_path),
    )
    second = extract_country_pbf(
        _acquisition(tmp_path),
        bounds,
        tmp_path / "cache",
        osmium_binary=_binary(tmp_path),
    )

    assert first.cache_hit is False
    assert second.cache_hit is True
    assert first.content_sha256 == second.content_sha256
    assert len(calls) == 1
    assert "--strategy=complete_ways" in calls[0]


def test_rejects_unapproved_binary_path(tmp_path: Path) -> None:
    with pytest.raises(PbfDownloadRejected, match="not approved"):
        extract_country_pbf(
            _acquisition(tmp_path),
            GeoBounds(-10, -10, 10, 10),
            tmp_path / "cache",
            osmium_binary=Path("osmium"),
        )


def test_rejects_failed_extraction(
    tmp_path: Path,
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    def fake_run(args: list[str], **_kwargs: object) -> subprocess.CompletedProcess[str]:
        return subprocess.CompletedProcess(args, 2, "", "failed")

    monkeypatch.setattr(subprocess, "run", fake_run)
    with pytest.raises(PbfDownloadRejected, match="nonzero"):
        extract_country_pbf(
            _acquisition(tmp_path),
            GeoBounds(-10, -10, 10, 10),
            tmp_path / "cache",
            osmium_binary=_binary(tmp_path),
        )


def test_binary_must_be_executable(tmp_path: Path) -> None:
    binary = tmp_path / "osmium"
    binary.write_text("binary")
    os.chmod(binary, 0o600)
    with pytest.raises(PbfDownloadRejected, match="executable"):
        extract_country_pbf(
            _acquisition(tmp_path),
            GeoBounds(-10, -10, 10, 10),
            tmp_path / "cache",
            osmium_binary=binary,
        )
