"""Tests for web GUI API endpoints."""
import json

import pytest

try:
    from flask import Flask
    FLASK_AVAILABLE = True
except ImportError:
    FLASK_AVAILABLE = False


pytestmark = pytest.mark.skipif(
    not FLASK_AVAILABLE,
    reason="Flask not installed - skip API tests"
)


@pytest.fixture
def app(mock_db_path, db_with_replays, mock_config_dir):
    """Create Flask test app with test database and isolated config."""
    from sc2_replay_analyzer.server import create_app

    app = create_app()
    app.config["TESTING"] = True
    return app


@pytest.fixture
def client(app):
    """Flask test client."""
    return app.test_client()


# ============================================================
# REPLAYS ENDPOINT
# ============================================================

class TestReplaysEndpoint:
    def test_returns_replays_with_summary(self, client):
        response = client.get("/api/v1/replays")
        assert response.status_code == 200
        data = response.get_json()

        assert "replays" in data
        assert "summary" in data
        assert "total" in data
        assert data["total"] > 0

    def test_summary_contains_expected_fields(self, client):
        data = client.get("/api/v1/replays").get_json()
        summary = data["summary"]
        assert "wins" in summary
        assert "losses" in summary
        assert "winrate" in summary

    def test_filter_by_matchup(self, client):
        data = client.get("/api/v1/replays?matchup=TvZ").get_json()
        for replay in data["replays"]:
            assert replay["matchup"].upper() == "TVZ"

    def test_filter_by_result(self, client):
        data = client.get("/api/v1/replays?result=Win").get_json()
        for replay in data["replays"]:
            assert replay["result"].lower() == "win"

    def test_filter_by_limit(self, client):
        data = client.get("/api/v1/replays?limit=1").get_json()
        assert len(data["replays"]) <= 1

    def test_filter_by_map(self, client):
        data = client.get("/api/v1/replays?map=Alcyone").get_json()
        for replay in data["replays"]:
            assert "Alcyone" in replay["map_name"]

    def test_filter_by_player_race(self, client):
        data = client.get("/api/v1/replays?player_race=Terran").get_json()
        for replay in data["replays"]:
            assert replay["player_race"].lower() == "terran"

    def test_filter_is_ladder_default_true(self, client):
        """Default is_ladder=true should include NULL (pre-migration) data."""
        data = client.get("/api/v1/replays").get_json()
        # Our test data has is_ladder=1 so it should be included
        assert data["total"] > 0

    def test_filter_is_ladder_false(self, client):
        """is_ladder=false should exclude ladder games."""
        data = client.get("/api/v1/replays?is_ladder=false").get_json()
        # All our test data is ladder=1, so nothing should match
        assert data["total"] == 0


# ============================================================
# LATEST ENDPOINT
# ============================================================

class TestLatestEndpoint:
    def test_returns_latest_replay(self, client):
        data = client.get("/api/v1/replays/latest").get_json()
        assert "replay" in data
        assert data["replay"] is not None
        assert "replay_id" in data["replay"]


# ============================================================
# STATS ENDPOINT
# ============================================================

class TestStatsEndpoint:
    def test_returns_stats(self, client):
        data = client.get("/api/v1/stats").get_json()
        assert "stats" in data
        assert "matchup_stats" in data
        assert data["stats"]["total_games"] > 0

    def test_stats_with_matchup_filter(self, client):
        data = client.get("/api/v1/stats?matchup=TvZ").get_json()
        assert data["matchup_stats"] == []

    def test_stats_with_player_race_filter(self, client):
        data = client.get("/api/v1/stats?player_race=Terran").get_json()
        assert data["stats"]["total_games"] > 0


# ============================================================
# MAPS & RACES
# ============================================================

class TestMapsEndpoint:
    def test_returns_map_names(self, client):
        data = client.get("/api/v1/maps").get_json()
        assert "maps" in data
        assert "Alcyone LE" in data["maps"]


class TestRacesEndpoint:
    def test_returns_race_distribution(self, client):
        data = client.get("/api/v1/races").get_json()
        assert "races" in data
        assert "default" in data
        assert isinstance(data["races"], list)

    def test_default_race_is_most_played(self, client):
        data = client.get("/api/v1/races").get_json()
        assert data["default"] == "Terran"


# ============================================================
# TAGS ENDPOINT
# ============================================================

class TestTagsEndpoint:
    def test_get_tags_empty(self, client):
        data = client.get("/api/v1/tags").get_json()
        assert "tags" in data
        assert isinstance(data["tags"], list)

    def test_create_tag(self, client):
        response = client.post("/api/v1/tags",
            data=json.dumps({"label": "Test Tag", "date": "2024-12-15"}),
            content_type="application/json")
        assert response.status_code == 201
        assert response.get_json()["success"] is True

    def test_create_tag_requires_label(self, client):
        response = client.post("/api/v1/tags",
            data=json.dumps({}), content_type="application/json")
        assert response.status_code == 400

    def test_create_duplicate_tag(self, client):
        client.post("/api/v1/tags",
            data=json.dumps({"label": "Dup", "date": "2024-12-15", "end_date": "2024-12-15"}),
            content_type="application/json")
        response = client.post("/api/v1/tags",
            data=json.dumps({"label": "Dup", "date": "2024-12-15"}),
            content_type="application/json")
        assert response.status_code == 409

    def test_end_tag(self, client):
        client.post("/api/v1/tags",
            data=json.dumps({"label": "End Me", "date": "2024-12-01"}),
            content_type="application/json")
        tags = client.get("/api/v1/tags").get_json()["tags"]
        tag = next(t for t in tags if t["label"] == "End Me")

        response = client.put(f"/api/v1/tags/{tag['id']}/end",
            data=json.dumps({"end_date": "2024-12-15"}),
            content_type="application/json")
        assert response.status_code == 200

    def test_end_nonexistent_tag(self, client):
        response = client.put("/api/v1/tags/99999/end",
            data=json.dumps({}), content_type="application/json")
        assert response.status_code == 404

    def test_delete_tag(self, client):
        client.post("/api/v1/tags",
            data=json.dumps({"label": "Delete Me", "date": "2024-12-05", "end_date": "2024-12-05"}),
            content_type="application/json")
        tags = client.get("/api/v1/tags").get_json()["tags"]
        tag = next(t for t in tags if t["label"] == "Delete Me")

        response = client.delete(f"/api/v1/tags/{tag['id']}")
        assert response.status_code == 200

        labels = [t["label"] for t in client.get("/api/v1/tags").get_json()["tags"]]
        assert "Delete Me" not in labels

    def test_tag_type_classification(self, client):
        client.post("/api/v1/tags",
            data=json.dumps({"label": "Ongoing", "date": "2024-12-01"}),
            content_type="application/json")
        client.post("/api/v1/tags",
            data=json.dumps({"label": "Single", "date": "2024-12-05", "end_date": "2024-12-05"}),
            content_type="application/json")

        tags = client.get("/api/v1/tags").get_json()["tags"]
        ongoing = next(t for t in tags if t["label"] == "Ongoing")
        single = next(t for t in tags if t["label"] == "Single")

        assert ongoing["type"] == "ongoing"
        assert single["type"] == "single"


# ============================================================
# COLUMNS ENDPOINT
# ============================================================

class TestColumnsEndpoint:
    def test_get_columns(self, client):
        data = client.get("/api/v1/columns").get_json()
        assert "current" in data
        assert "available" in data
        assert len(data["current"]) > 0

    def test_update_columns(self, client):
        response = client.put("/api/v1/columns",
            data=json.dumps({"columns": ["date", "map", "result"]}),
            content_type="application/json")
        assert response.status_code == 200
        assert client.get("/api/v1/columns").get_json()["current"] == ["date", "map", "result"]

    def test_update_columns_invalid(self, client):
        response = client.put("/api/v1/columns",
            data=json.dumps({"columns": ["not_real"]}),
            content_type="application/json")
        assert response.status_code == 400

    def test_update_columns_requires_body(self, client):
        response = client.put("/api/v1/columns",
            data=json.dumps({}), content_type="application/json")
        assert response.status_code == 400


# ============================================================
# CONFIG ENDPOINT
# ============================================================

class TestConfigEndpoint:
    def test_returns_config(self, client):
        data = client.get("/api/v1/config").get_json()
        assert "player_name" in data
        assert "region" in data
        assert "benchmarks" in data
        assert "auto_scan_interval_ms" in data

    def test_update_config(self, client):
        response = client.put("/api/v1/config",
            data=json.dumps({"player_name": "NewName", "region": "us"}),
            content_type="application/json")
        assert response.status_code == 200

        data = client.get("/api/v1/config").get_json()
        assert data["player_name"] == "NewName"
        assert data["region"] == "us"


# ============================================================
# SCAN ENDPOINT
# ============================================================

class TestScanEndpoint:
    def test_trigger_scan(self, client, mock_db_path):
        data = client.post("/api/v1/scan").get_json()
        assert "new_replays" in data
        assert isinstance(data["new_replays"], int)


# ============================================================
# MMR HISTORY (bug fix: filter support)
# ============================================================

class TestMmrHistoryEndpoint:
    def test_returns_data(self, client):
        data = client.get("/api/v1/mmr/history").get_json()
        assert "data" in data
        assert "player_name" in data
        assert "tags" in data

    def test_respects_days_filter(self, client):
        """Bug fix: MMR history should respect day filter."""
        all_data = client.get("/api/v1/mmr/history").get_json()
        # With days=1 (today only), should return fewer or equal games
        filtered = client.get("/api/v1/mmr/history?days=1").get_json()
        assert len(filtered["data"]) <= len(all_data["data"])

    def test_respects_matchup_filter(self, client):
        """Bug fix: MMR history should respect matchup filter."""
        filtered = client.get("/api/v1/mmr/history?matchup=TvZ").get_json()
        for entry in filtered["data"]:
            assert entry["matchup"].upper() == "TVZ"

    def test_respects_player_race_filter(self, client):
        filtered = client.get("/api/v1/mmr/history?player_race=Terran").get_json()
        assert isinstance(filtered["data"], list)


# ============================================================
# REPLAY NOTES (Post Game Analysis)
# ============================================================

class TestNotesEndpoints:
    def test_get_notes_empty(self, client):
        data = client.get("/api/v1/replays/abc123def456/notes").get_json()
        assert "notes" in data
        assert data["notes"] is None

    def test_save_and_get_notes(self, client):
        analysis = {"opponent_strat": "2-base all-in", "mistake_1": "Late scout"}

        response = client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({"analysis": analysis}),
            content_type="application/json")
        assert response.status_code == 200

        data = client.get("/api/v1/replays/abc123def456/notes").get_json()
        assert data["notes"] is not None
        assert data["notes"]["analysis"]["opponent_strat"] == "2-base all-in"

    def test_update_notes(self, client):
        client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({"analysis": {"mistake_1": "Original"}}),
            content_type="application/json")
        client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({"analysis": {"mistake_1": "Updated"}}),
            content_type="application/json")

        data = client.get("/api/v1/replays/abc123def456/notes").get_json()
        assert data["notes"]["analysis"]["mistake_1"] == "Updated"

    def test_delete_notes(self, client):
        client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({"analysis": {"test": "data"}}),
            content_type="application/json")
        response = client.delete("/api/v1/replays/abc123def456/notes")
        assert response.status_code == 200

        data = client.get("/api/v1/replays/abc123def456/notes").get_json()
        assert data["notes"] is None

    def test_delete_nonexistent_notes(self, client):
        response = client.delete("/api/v1/replays/nonexistent/notes")
        assert response.status_code == 404

    def test_save_notes_requires_analysis(self, client):
        response = client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({}), content_type="application/json")
        assert response.status_code == 400

    def test_notes_feed(self, client):
        # Create a note first
        client.put("/api/v1/replays/abc123def456/notes",
            data=json.dumps({"analysis": {"opponent_strat": "Proxy rax"}}),
            content_type="application/json")

        data = client.get("/api/v1/notes/feed").get_json()
        assert "notes" in data
        assert len(data["notes"]) > 0
        note = data["notes"][0]
        assert "analysis" in note
        assert "played_at" in note
        assert "map_name" in note

    def test_notes_feed_with_limit(self, client):
        data = client.get("/api/v1/notes/feed?limit=1").get_json()
        assert len(data["notes"]) <= 1


# ============================================================
# ANALYSIS CONFIG
# ============================================================

class TestAnalysisConfigEndpoint:
    def test_returns_fields(self, client):
        data = client.get("/api/v1/analysis/config").get_json()
        assert "fields" in data
        assert isinstance(data["fields"], list)
        assert len(data["fields"]) > 0
        assert "key" in data["fields"][0]
        assert "label" in data["fields"][0]
        assert "type" in data["fields"][0]


# ============================================================
# GUI & OVERLAY PAGES
# ============================================================

class TestDraftEndpoints:
    """Tests for live analysis draft endpoints."""

    def test_get_draft_initially_inactive(self, client):
        data = client.get("/api/v1/analysis/draft").get_json()
        assert data["active"] is False

    def test_set_and_get_draft(self, client):
        client.put("/api/v1/analysis/draft",
            data=json.dumps({
                "replay": {"result": "Win", "matchup": "TvZ", "map_name": "Alcyone"},
                "analysis": {"opponent_strat": "Roach all-in"},
            }),
            content_type="application/json")

        data = client.get("/api/v1/analysis/draft").get_json()
        assert data["active"] is True
        assert data["replay"]["matchup"] == "TvZ"
        assert data["analysis"]["opponent_strat"] == "Roach all-in"

    def test_clear_draft(self, client):
        client.put("/api/v1/analysis/draft",
            data=json.dumps({"replay": {"result": "Win"}, "analysis": {}}),
            content_type="application/json")
        client.delete("/api/v1/analysis/draft")

        data = client.get("/api/v1/analysis/draft").get_json()
        assert data["active"] is False

    def test_draft_updates_on_each_put(self, client):
        client.put("/api/v1/analysis/draft",
            data=json.dumps({"replay": {}, "analysis": {"mistake_1": "First"}}),
            content_type="application/json")
        client.put("/api/v1/analysis/draft",
            data=json.dumps({"replay": {}, "analysis": {"mistake_1": "Updated"}}),
            content_type="application/json")

        data = client.get("/api/v1/analysis/draft").get_json()
        assert data["analysis"]["mistake_1"] == "Updated"


class TestGuiRoutes:
    def test_index_returns_html(self, client):
        response = client.get("/")
        assert response.status_code == 200
        assert b"SC2 Replay Analyzer" in response.data

    def test_analysis_page_returns_html(self, client):
        response = client.get("/analysis")
        assert response.status_code == 200
        assert b"Analysis Feed" in response.data

    def test_stream_widget_returns_html(self, client):
        response = client.get("/overlays/stream-widget")
        assert response.status_code == 200
        assert b"widget" in response.data.lower()

    def test_analysis_widget_returns_html(self, client):
        response = client.get("/overlays/analysis-widget")
        assert response.status_code == 200
        assert b"analysis" in response.data.lower()

    def test_analysis_live_returns_html(self, client):
        response = client.get("/overlays/analysis-live")
        assert response.status_code == 200
        assert b"live" in response.data.lower()

    def test_mmr_graph_overlay_still_works(self, client):
        response = client.get("/overlays/mmr-graph")
        assert response.status_code == 200
        assert b"mmrChart" in response.data
