"""
API tests for the segmentation v2 router (/api/segmentation/*).

Tests:
- GET /api/segmentation/status -- checkpoint availability check
- POST /api/segmentation/segment -- single H5 file segmentation (mocked runner)
- POST /api/segmentation/segment/batch -- batch directory (mocked runner)
- Error cases: missing file, bad extension, model unavailable

Run with:
    pytest tests/test_segmentation_api.py -v
"""

from __future__ import annotations

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

torch = pytest.importorskip("torch")
from fastapi.testclient import TestClient


@pytest.fixture()
def client():
    """Create a test client for the SLTK API."""
    from sltk.api.main import app

    return TestClient(app)


# ===========================================================================
# GET /api/segmentation/status
# ===========================================================================


class TestSegmentationStatus:
    """Test the status endpoint."""

    def test_status_returns_200(self, client):
        resp = client.get("/api/segmentation/status")
        assert resp.status_code == 200
        data = resp.json()
        assert "checkpoint_exists" in data
        assert "gpu_available" in data

    def test_status_reports_checkpoint(self, client):
        resp = client.get("/api/segmentation/status")
        data = resp.json()
        # checkpoint_exists is bool based on whether file is on disk
        assert isinstance(data["checkpoint_exists"], bool)


# ===========================================================================
# POST /api/segmentation/segment
# ===========================================================================


class TestSegmentSingle:
    """Test single H5 file segmentation endpoint."""

    def test_segment_with_mocked_runner(self, client, tmp_path):
        """Mock the segmentation pipeline and verify endpoint plumbing."""
        # Create a fake H5 file
        h5_file = tmp_path / "test_video.h5"
        h5_file.touch()

        fake_features = torch.randn(100, 192)
        fake_labels = torch.tensor([0] * 10 + [2] + [1] * 20 + [0] * 10 + [2] + [1] * 15 + [0] * 43)

        with (
            patch(
                "sltk.api.routers.segmentation.validate_path",
                return_value=h5_file,
            ),
            patch(
                "sltk.segmentation.h5_loader.h5_to_features",
                return_value=fake_features,
            ),
            patch(
                "sltk.segmentation.runner.get_runner",
            ) as mock_get_runner,
        ):
            mock_runner = MagicMock()
            mock_runner.predict.return_value = fake_labels
            mock_get_runner.return_value = mock_runner

            resp = client.post(
                "/api/segmentation/segment",
                json={"h5_path": str(h5_file)},
            )

        assert resp.status_code == 200
        data = resp.json()
        assert data["video_name"] == "test_video"
        assert data["num_frames"] == 100
        assert len(data["segments"]) == 2
        assert len(data["labels"]) == 100

    def test_segment_rejects_non_h5(self, client, tmp_path):
        """Non-.h5 files should be rejected."""
        bad_file = tmp_path / "video.mp4"
        bad_file.touch()

        with patch(
            "sltk.api.routers.segmentation.validate_path",
            return_value=bad_file,
        ):
            resp = client.post(
                "/api/segmentation/segment",
                json={"h5_path": str(bad_file)},
            )

        assert resp.status_code == 400
        assert "H5" in resp.json()["detail"]

    def test_segment_model_unavailable(self, client, tmp_path):
        """When model checkpoint is missing, return 503."""
        h5_file = tmp_path / "test.h5"
        h5_file.touch()

        with (
            patch(
                "sltk.api.routers.segmentation.validate_path",
                return_value=h5_file,
            ),
            patch(
                "sltk.segmentation.h5_loader.h5_to_features",
                return_value=torch.randn(50, 192),
            ),
            patch(
                "sltk.segmentation.runner.get_runner",
                side_effect=FileNotFoundError("No checkpoint"),
            ),
        ):
            resp = client.post(
                "/api/segmentation/segment",
                json={"h5_path": str(h5_file)},
            )

        assert resp.status_code == 503
        assert "unavailable" in resp.json()["detail"].lower()


# ===========================================================================
# POST /api/segmentation/segment/batch
# ===========================================================================


class TestSegmentBatch:
    """Test batch directory segmentation endpoint."""

    def test_batch_with_mocked_runner(self, client, tmp_path):
        """Mock the runner and verify batch endpoint plumbing."""
        h5_dir = tmp_path / "h5_files"
        h5_dir.mkdir()
        (h5_dir / "a.h5").touch()
        (h5_dir / "b.h5").touch()

        predictions = {
            "a": torch.tensor([0, 2, 1, 1, 0]),
            "b": torch.tensor([2, 1, 0, 0, 0]),
        }

        with (
            patch(
                "sltk.api.routers.segmentation.validate_path",
                return_value=h5_dir,
            ),
            patch(
                "sltk.segmentation.runner.get_runner",
            ) as mock_get_runner,
        ):
            mock_runner = MagicMock()
            mock_runner.run_h5.return_value = predictions
            mock_get_runner.return_value = mock_runner

            resp = client.post(
                "/api/segmentation/segment/batch",
                json={"directory": str(h5_dir)},
            )

        assert resp.status_code == 200
        data = resp.json()
        assert data["total_videos"] == 2
        assert "a" in data["results"]
        assert "b" in data["results"]

    def test_batch_rejects_non_directory(self, client, tmp_path):
        """Non-directory paths should be rejected."""
        regular_file = tmp_path / "not_a_dir.txt"
        regular_file.touch()

        with patch(
            "sltk.api.routers.segmentation.validate_path",
            return_value=regular_file,
        ):
            resp = client.post(
                "/api/segmentation/segment/batch",
                json={"directory": str(regular_file)},
            )

        assert resp.status_code == 400
        assert "directory" in resp.json()["detail"].lower()

    def test_batch_rejects_invalid_format(self, client, tmp_path):
        """Invalid output_format should be rejected."""
        h5_dir = tmp_path / "h5s"
        h5_dir.mkdir()

        with patch(
            "sltk.api.routers.segmentation.validate_path",
            return_value=h5_dir,
        ):
            resp = client.post(
                "/api/segmentation/segment/batch",
                json={"directory": str(h5_dir), "output_format": "csv"},
            )

        assert resp.status_code == 400
        assert "output_format" in resp.json()["detail"].lower()
