"""Tests for the canonical LogRecord schema (Wave 1)."""

from __future__ import annotations

import json
import logging
import sys

from mgf.common.observability import LOG_SCHEMA_VERSION, LogRecord


def _py_record(
    name: str = "myapp.db",
    level: int = logging.INFO,
    msg: str = "hello %s",
    args: tuple = ("world",),
    exc_info=None,
) -> logging.LogRecord:
    return logging.LogRecord(name, level, "/x/y.py", 10, msg, args, exc_info, func="f")


class TestFromPython:
    def test_core_fields(self) -> None:
        lr = LogRecord.from_python(_py_record(), app="myapp", app_version="1.0")
        assert lr.logger == "myapp.db"
        assert lr.level == "INFO"
        assert lr.level_no == logging.INFO
        assert lr.msg == "hello world"  # %-args rendered
        assert lr.app == "myapp"
        assert lr.app_version == "1.0"
        assert lr.lang == "python"
        assert lr.line == 10
        assert lr.func == "f"

    def test_extras_harvested_into_fields(self) -> None:
        rec = _py_record()
        rec.user_id = 42
        rec.tenant = "acme"
        lr = LogRecord.from_python(rec)
        assert lr.fields == {"user_id": 42, "tenant": "acme"}

    def test_stdlib_attrs_excluded_from_fields(self) -> None:
        lr = LogRecord.from_python(_py_record())
        # None of the standard record attrs leak into `fields`.
        for noisy in ("levelname", "levelno", "msg", "args", "created", "name"):
            assert noisy not in lr.fields

    def test_correlation_pulled_into_typed_slots(self) -> None:
        rec = _py_record()
        rec.trace_id = "abc123"
        rec.request_id = "req-9"
        lr = LogRecord.from_python(rec)
        assert lr.trace_id == "abc123"
        assert lr.request_id == "req-9"
        # ...and not duplicated into fields
        assert "trace_id" not in lr.fields
        assert "request_id" not in lr.fields

    def test_exception_captured(self) -> None:
        try:
            raise ValueError("boom")
        except ValueError:
            rec = _py_record(level=logging.ERROR, msg="failed", args=(), exc_info=sys.exc_info())
        lr = LogRecord.from_python(rec)
        assert lr.exception is not None
        assert "ValueError: boom" in lr.exception


class TestSerialization:
    def test_jsonl_roundtrip(self) -> None:
        rec = _py_record()
        rec.user_id = 7
        lr = LogRecord.from_python(rec, app="a", app_version="2")
        rt = LogRecord.from_jsonl_line(lr.to_jsonl_line())
        assert rt.to_dict() == lr.to_dict()

    def test_core_keys_always_present(self) -> None:
        d = json.loads(LogRecord.from_python(_py_record(), app="a").to_jsonl_line())
        for key in ("ts", "level", "level_no", "logger", "msg", "app", "lang", "schema_version"):
            assert key in d

    def test_empty_optionals_omitted(self) -> None:
        # No exception / trace / env / labels → those keys absent (compact wire).
        d = json.loads(LogRecord.from_python(_py_record()).to_jsonl_line())
        for absent in ("exception", "stack", "trace_id", "env", "labels"):
            assert absent not in d

    def test_schema_version_default(self) -> None:
        assert LogRecord.from_python(_py_record()).schema_version == LOG_SCHEMA_VERSION

    def test_from_dict_tolerates_unknown_keys(self) -> None:
        line = json.dumps(
            {
                "ts": "2026-05-28T00:00:00+00:00",
                "level": "INFO",
                "level_no": 20,
                "logger": "x",
                "msg": "m",
                "app": "a",
                "lang": "python",
                "future_field": "v",  # newer schema
            }
        )
        lr = LogRecord.from_jsonl_line(line)
        assert lr.msg == "m"
        assert lr.logger == "x"


class TestExtensibility:
    """The schema must grow without redesign or breakage (OB-09)."""

    def _base(self, **extra) -> dict:
        d = {
            "ts": "2026-05-28T00:00:00+00:00",
            "level": "INFO",
            "level_no": 20,
            "logger": "x",
            "msg": "m",
            "app": "a",
            "lang": "python",
        }
        d.update(extra)
        return d

    def test_unknown_top_level_key_is_preserved_losslessly(self) -> None:
        """Rule 3: a newer-schema key survives a parse → re-serialize
        round-trip (an older relay must not strip it)."""
        line = json.dumps(self._base(geo="us-east", priority=5))
        lr = LogRecord.from_jsonl_line(line)
        assert lr.extra == {"geo": "us-east", "priority": 5}
        out = json.loads(lr.to_jsonl_line())
        assert out["geo"] == "us-east"
        assert out["priority"] == 5

    def test_extra_never_clobbers_a_known_key(self) -> None:
        lr = LogRecord.from_python(_py_record(), app="real")
        lr.extra["app"] = "spoofed"  # known key sneaked into extra
        assert json.loads(lr.to_jsonl_line())["app"] == "real"

    def test_backward_compat_missing_newer_fields_default(self) -> None:
        """An OLD record lacking newer fields parses with defaults, no crash."""
        lr = LogRecord.from_dict({"ts": "t", "level": "INFO", "logger": "x", "msg": "m"})
        assert lr.app == ""
        assert lr.env is None
        assert lr.labels == {}
        assert lr.level_no == 0

    def test_fields_is_the_zero_schema_change_extension_path(self) -> None:
        """Adding arbitrary structured data needs no schema change."""
        rec = _py_record()
        rec.anything_new = {"nested": [1, 2]}
        lr = LogRecord.from_python(rec)
        assert lr.fields["anything_new"] == {"nested": [1, 2]}
        rt = LogRecord.from_jsonl_line(lr.to_jsonl_line())
        assert rt.fields["anything_new"] == {"nested": [1, 2]}
