import pytest
from typer.testing import CliRunner
from qrm.query_commands import query_app
from qrm.client import QrmClientError, LoginState
from qrm.commands_shared import OutputFormat

runner = CliRunner()

_FAKE_STATE = LoginState(
    token="TOKEN123",
    username="alice",
    base_url="https://qrm.local/api",
    verify_tls=True,
)


def test_results_rich(monkeypatch):
    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return [
                {
                    "ResultSetName": "foo",
                    "SerialNumber": "bar",
                    "Outcome": "Passed",
                    "StartDateTime": "2024-01-01T00:00:00",
                    "StopDateTime": "2024-01-01T01:00:00",
                    "StationName": "A",
                    "OperatorName": "B",
                    "TestProgramName": "prog",
                    "TestTypeName": "type",
                    "SequenceName": "seq",
                    "TestSocketIndex": 1,
                    "LogFileURL": "url",
                    "InsertDateTime": "2024-01-01T02:00:00",
                    "Yield": 1,
                    "UUTTypeName": "uut",
                }
            ]

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["results", "--output", "rich"])
    assert result.exit_code == 0
    # Check for table grid and data, not full header string
    assert "┏" in result.output or "━" in result.output  # Table border
    assert "foo" in result.output or "━" in result.output  # Data value


def test_serial_rich(monkeypatch):
    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return [
                {
                    "ResultSetName": "foo",
                    "SerialNumber": "bar",
                    "Outcome": "Passed",
                    "StartDateTime": "2024-01-01T00:00:00",
                    "StopDateTime": "2024-01-01T01:00:00",
                    "StationName": "A",
                    "OperatorName": "B",
                    "TestProgramName": "prog",
                    "TestTypeName": "type",
                    "SequenceName": "seq",
                    "TestSocketIndex": 1,
                    "LogFileURL": "url",
                    "InsertDateTime": "2024-01-01T02:00:00",
                    "Yield": 1,
                    "UUTTypeName": "uut",
                }
            ]

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["serial", "sn", "--output", "rich"])
    assert result.exit_code == 0
    # Check for table grid and data, not full header string
    assert "┏" in result.output or "━" in result.output  # Table border
    assert "foo" in result.output or "━" in result.output or "bar" in result.output  # Data value


def test_serial_qrmclienterror(monkeypatch):
    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            raise QrmClientError("fail")

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["serial", "sn"])
    assert result.exit_code != 0
    assert "query failed" in result.output


_FPY_PAYLOAD = [
    {"StationName": "RYW1036", "SequenceName": "Main", "Total": 10, "Passed": 8, "Failed": 2},
    {"StationName": "RYW1037", "SequenceName": "Final", "Total": 5, "Passed": 5, "Failed": 0},
]


def test_fpy_rich(monkeypatch):
    """FPY command renders a Rich table with station/sequence rows."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _FPY_PAYLOAD

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy", "--output", "rich"])
    assert result.exit_code == 0
    assert "RYW1036" in result.output
    assert "80.0" in result.output  # FPY for 8/10


def test_fpy_rich_shows_window(monkeypatch):
    """FPY rich output includes the time window as a caption."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _FPY_PAYLOAD

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(
        query_app,
        [
            "fpy",
            "--start",
            "2026-03-20T00:00:00",
            "--stop",
            "2026-03-26T23:59:59",
            "--output",
            "rich",
        ],
    )
    assert result.exit_code == 0
    assert "2026-03-20" in result.output
    assert "2026-03-26" in result.output


def test_fpy_json(monkeypatch):
    """FPY command emits JSON envelope with start/stop metadata and rows."""
    import json

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _FPY_PAYLOAD

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy", "--output", "json"])
    assert result.exit_code == 0
    data = json.loads(result.output)
    assert "start" in data
    assert "stop" in data
    assert isinstance(data["rows"], list)
    assert data["rows"][0]["StationName"] == "RYW1036"


def test_fpy_defaults_to_last_week(monkeypatch):
    """FPY command defaults time window to last 7 days."""
    import datetime

    captured = {}

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, token, query, format, parameters):
            captured["parameters"] = parameters
            captured["query"] = query
            return []

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy"])
    assert result.exit_code == 0
    assert "start" in captured["parameters"]
    assert "stop" in captured["parameters"]
    assert "GROUP BY" in captured["query"]
    # start should be 7 days before stop (roughly)
    from qrm.commands_shared import API_DATE_FORMAT

    start_dt = datetime.datetime.strptime(captured["parameters"]["start"], API_DATE_FORMAT)
    stop_dt = datetime.datetime.strptime(captured["parameters"]["stop"], API_DATE_FORMAT)
    assert (stop_dt - start_dt).days >= 6


def test_fpy_custom_window(monkeypatch):
    """FPY command respects explicit --start/--stop."""
    captured = {}

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, token, query, format, parameters):
            captured["parameters"] = parameters
            return []

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(
        query_app, ["fpy", "--start", "2026-03-01T00:00:00", "--stop", "2026-03-07T23:59:59"]
    )
    assert result.exit_code == 0
    assert captured["parameters"]["start"] == "2026-03-01 00:00:00"
    assert captured["parameters"]["stop"] == "2026-03-07 23:59:59"


def test_fpy_invalid_date(monkeypatch):
    """FPY command exits 1 on invalid date format."""
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy", "--start", "not-a-date"])
    assert result.exit_code != 0
    assert "Invalid date format" in result.output


def test_fpy_qrmclienterror(monkeypatch):
    """FPY command surfaces QrmClientError with exit code 1."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            raise QrmClientError("fail")

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy"])
    assert result.exit_code != 0
    assert "query failed" in result.output


def test_fpy_empty_results(monkeypatch):
    """FPY command shows 'No results' when no data returned."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return []

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["fpy", "--output", "rich"])
    assert result.exit_code == 0
    assert "No results" in result.output


# ---------------------------------------------------------------------------
# test-duration
# ---------------------------------------------------------------------------

_AGG_DURATION_ROWS = [
    {
        "StationName": "RYW1036",
        "SequenceName": "AirGlow test",
        "Count": 541,
        "MinDur": 7,
        "AvgDur": 74.4,
        "MaxDur": 110,
        "ShortCount": 3,
    },
    {
        "StationName": "RYW1078",
        "SequenceName": "TimoTwo test and prog2 short",
        "Count": 1235,
        "MinDur": 29,
        "AvgDur": 64.7,
        "MaxDur": 81,
        "ShortCount": 0,
    },
]


def test_test_duration_rich(monkeypatch):
    """test-duration renders a Rich table with computed statistical columns."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _AGG_DURATION_ROWS

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration", "--output", "rich"])
    assert result.exit_code == 0
    assert "Test Duration" in result.output
    assert "RYW1036" in result.output
    assert "74.4" in result.output  # avg for RYW1036


def test_test_duration_shows_window(monkeypatch):
    """test-duration rich output includes the time window as a caption."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _AGG_DURATION_ROWS

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(
        query_app,
        [
            "test-duration",
            "--start",
            "2026-03-20T00:00:00",
            "--stop",
            "2026-03-26T23:59:59",
            "--output",
            "rich",
        ],
    )
    assert result.exit_code == 0
    assert "2026-03-20" in result.output
    assert "2026-03-26" in result.output


def test_test_duration_json(monkeypatch):
    """test-duration --output json returns envelope with start/stop/rows."""
    import json

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return _AGG_DURATION_ROWS

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration", "--output", "json"])
    assert result.exit_code == 0
    data = json.loads(result.output)
    assert "start" in data
    assert "stop" in data
    assert isinstance(data["rows"], list)
    row = data["rows"][0]
    assert row["StationName"] == "RYW1036"
    assert row["Count"] == 541
    assert row["ShortCount"] == 3


def test_test_duration_empty_results(monkeypatch):
    """test-duration shows 'No results' when no data returned."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            return []

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration", "--output", "rich"])
    assert result.exit_code == 0
    assert "No results" in result.output


def test_test_duration_invalid_date(monkeypatch):
    """test-duration exits 1 on invalid date format."""
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration", "--start", "not-a-date"])
    assert result.exit_code != 0
    assert "Invalid date format" in result.output


def test_test_duration_client_error(monkeypatch):
    """test-duration surfaces QrmClientError with exit code 1."""

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, **kwargs):
            raise QrmClientError("fail")

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration"])
    assert result.exit_code != 0
    assert "query failed" in result.output


def test_test_duration_defaults_to_last_week(monkeypatch):
    """test-duration defaults time window to last 7 days."""
    import datetime

    captured = {}

    class DummyClient:
        def __init__(self, *a, **k):
            pass

        def export_sql(self, token, query, format, parameters):
            captured["parameters"] = parameters
            return []

    monkeypatch.setattr("qrm.query_commands.QrmClient", DummyClient)
    monkeypatch.setattr("qrm.query_commands.load_state_or_exit", lambda *a, **k: _FAKE_STATE)
    result = runner.invoke(query_app, ["test-duration"])
    assert result.exit_code == 0
    assert "start" in captured["parameters"]
    assert "stop" in captured["parameters"]
    from qrm.commands_shared import API_DATE_FORMAT

    start_dt = datetime.datetime.strptime(captured["parameters"]["start"], API_DATE_FORMAT)
    stop_dt = datetime.datetime.strptime(captured["parameters"]["stop"], API_DATE_FORMAT)
    assert (stop_dt - start_dt).days >= 6
