"""Tests for `mgf-common logs` (Wave 2)."""

from __future__ import annotations

import argparse
import json
from typing import TYPE_CHECKING

import pytest
from mgf.common.cli._exit_codes import EXIT_CONFIG_ERROR, EXIT_SUCCESS

from mgf.fed.cli._logs import (
    _iter_records,
    _level_no,
    _matches,
    add_logs_args,
    run_logs,
)

if TYPE_CHECKING:
    from pathlib import Path


@pytest.fixture(autouse=True)
def _identity() -> None:
    # The CLI's main() sets process identity before dispatch; mirror that
    # here so the path helpers (default_*_dir) don't emit the pre-bootstrap
    # UnconfiguredWarning (which the suite escalates to an error).
    from mgf.common._identity import _set_identity

    _set_identity("test-app", "0.0.0")


def _args(**kw) -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    add_logs_args(parser)
    argv: list[str] = [kw.pop("action", "info")]
    for k, v in kw.items():
        flag = "--" + k.replace("_", "-")
        if v is True:
            argv.append(flag)
        elif v is not None:
            argv += [flag, str(v)]
    return parser.parse_args(argv)


def _seed_log(path: Path, records: list[dict]) -> None:
    path.write_text("".join(json.dumps(r) + "\n" for r in records), encoding="utf-8")


_SAMPLE = [
    {"ts": "t1", "level": "INFO", "logger": "app.db", "msg": "connected"},
    {"ts": "t2", "level": "DEBUG", "logger": "app.http", "msg": "GET /"},
    {"ts": "t3", "level": "WARNING", "logger": "app.db", "msg": "slow query"},
    {"ts": "t4", "level": "ERROR", "logger": "app.http", "msg": "500", "exception": "Boom"},
]


class TestHelpers:
    def test_level_no(self) -> None:
        assert _level_no("warning") == 30
        assert _level_no("ERROR") == 40
        assert _level_no(None) == 0
        assert _level_no("bogus") == 0

    def test_matches_level_threshold(self) -> None:
        rec = {"level": "INFO", "logger": "x"}
        assert _matches(rec, _level_no("DEBUG"), None) is True
        assert _matches(rec, _level_no("WARNING"), None) is False

    def test_matches_component(self) -> None:
        rec = {"level": "INFO", "logger": "app.db"}
        assert _matches(rec, 0, "db") is True
        assert _matches(rec, 0, "http") is False

    def test_iter_skips_malformed(self, tmp_path: Path) -> None:
        f = tmp_path / "a.log"
        f.write_text(
            '{"ts":"t","level":"INFO","logger":"x","msg":"ok"}\nNOT JSON\n\n', encoding="utf-8"
        )
        recs = list(_iter_records(f))
        assert len(recs) == 1
        assert recs[0]["msg"] == "ok"


class TestTail:
    def test_prints_last_n(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        rc = run_logs(_args(action="tail", file=f, lines=2))
        assert rc == EXIT_SUCCESS
        out = capsys.readouterr().out
        assert "slow query" in out and "500" in out
        assert "connected" not in out  # trimmed to last 2

    def test_level_threshold(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        run_logs(_args(action="tail", file=f, level="WARNING"))
        out = capsys.readouterr().out
        assert "slow query" in out and "500" in out
        assert "GET /" not in out and "connected" not in out

    def test_component_filter(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        run_logs(_args(action="tail", file=f, component="http"))
        out = capsys.readouterr().out
        assert "GET /" in out and "500" in out
        assert "connected" not in out

    def test_json_output_roundtrips(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE[:1])
        run_logs(_args(action="tail", file=f, json=True))
        line = capsys.readouterr().out.strip()
        assert json.loads(line)["logger"] == "app.db"

    def test_exception_rendered(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, [_SAMPLE[3]])
        run_logs(_args(action="tail", file=f))
        assert "Boom" in capsys.readouterr().out

    def test_missing_file_errors(self, tmp_path: Path, capsys) -> None:
        rc = run_logs(_args(action="tail", file=tmp_path / "nope.log"))
        assert rc == EXIT_CONFIG_ERROR
        assert "not found" in capsys.readouterr().err


class TestInfo:
    def test_reports_counts_and_components(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        rc = run_logs(_args(action="info", file=f))
        assert rc == EXIT_SUCCESS
        out = capsys.readouterr().out
        assert str(f) in out
        assert "app.db" in out and "app.http" in out
        assert "scanned 4 records" in out

    def test_json_payload(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        run_logs(_args(action="info", file=f, json=True))
        payload = json.loads(capsys.readouterr().out)
        assert payload["exists"] is True
        assert payload["scanned_records"] == 4
        assert payload["levels"]["ERROR"] == 1
        assert payload["components"]["app.db"] == 2

    def test_missing_file_is_not_an_error(self, tmp_path: Path, capsys) -> None:
        rc = run_logs(_args(action="info", file=tmp_path / "nope.log"))
        assert rc == EXIT_SUCCESS
        assert "MISSING" in capsys.readouterr().out


class TestBadLevel:
    def test_unknown_level_rejected(self, tmp_path: Path, capsys) -> None:
        f = tmp_path / "app.log"
        _seed_log(f, _SAMPLE)
        rc = run_logs(_args(action="tail", file=f, level="LOUD"))
        assert rc == EXIT_CONFIG_ERROR
        assert "unknown level" in capsys.readouterr().err
