"""Tests for audit trail and history command (Phase 5)."""

from __future__ import annotations

import json
from pathlib import Path

import pytest
from typer.testing import CliRunner

from ilum.cli.main import app
from ilum.core.audit import AuditEntry, AuditLog


class TestAuditEntry:
    def test_defaults(self) -> None:
        entry = AuditEntry(
            timestamp="2025-01-15T10:30:00",
            operation="install",
            release="ilum",
            namespace="default",
            context="minikube",
            user="testuser",
            chart_version="6.7.0",
        )
        assert entry.success is True
        assert entry.modules == []
        assert entry.set_flags == []
        assert entry.error_code == ""


class TestAuditLog:
    def test_append_and_query(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        entry = AuditEntry(
            timestamp="2025-01-15T10:30:00",
            operation="install",
            release="ilum",
            namespace="default",
            context="minikube",
            user="test",
            chart_version="6.7.0",
            modules=["core", "ui"],
        )
        log.append(entry)

        entries = log.query()
        assert len(entries) == 1
        assert entries[0].operation == "install"
        assert entries[0].modules == ["core", "ui"]

    def test_append_only_jsonl(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        for i in range(3):
            log.append(
                AuditEntry(
                    timestamp=f"2025-01-{15 + i}T10:00:00",
                    operation="upgrade",
                    release="ilum",
                    namespace="default",
                    context="minikube",
                    user="test",
                    chart_version="6.7.0",
                )
            )

        entries = log.query()
        assert len(entries) == 3

        # Verify JSONL format
        lines = log.path.read_text().strip().split("\n")
        assert len(lines) == 3
        for line in lines:
            json.loads(line)  # Should not raise

    def test_query_filter_release(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        log.append(
            AuditEntry(
                timestamp="t1",
                operation="install",
                release="ilum-prod",
                namespace="prod",
                context="",
                user="",
                chart_version="",
            )
        )
        log.append(
            AuditEntry(
                timestamp="t2",
                operation="install",
                release="ilum-staging",
                namespace="staging",
                context="",
                user="",
                chart_version="",
            )
        )

        entries = log.query(release="ilum-prod")
        assert len(entries) == 1
        assert entries[0].release == "ilum-prod"

    def test_query_filter_operation(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        log.append(
            AuditEntry(
                timestamp="t1",
                operation="install",
                release="ilum",
                namespace="default",
                context="",
                user="",
                chart_version="",
            )
        )
        log.append(
            AuditEntry(
                timestamp="t2",
                operation="upgrade",
                release="ilum",
                namespace="default",
                context="",
                user="",
                chart_version="",
            )
        )

        entries = log.query(operation="upgrade")
        assert len(entries) == 1
        assert entries[0].operation == "upgrade"

    def test_query_last_n(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        for i in range(10):
            log.append(
                AuditEntry(
                    timestamp=f"t{i}",
                    operation="upgrade",
                    release="ilum",
                    namespace="default",
                    context="",
                    user="",
                    chart_version="",
                )
            )

        entries = log.query(last_n=5)
        assert len(entries) == 5
        assert entries[0].timestamp == "t5"

    def test_empty_log(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        entries = log.query()
        assert entries == []

    def test_malformed_lines_skipped(self, tmp_path: Path) -> None:
        log = AuditLog(tmp_path)
        log.append(
            AuditEntry(
                timestamp="t1",
                operation="install",
                release="ilum",
                namespace="default",
                context="",
                user="",
                chart_version="",
            )
        )
        # Add a malformed line
        with log.path.open("a") as f:
            f.write("not json\n")
        log.append(
            AuditEntry(
                timestamp="t2",
                operation="upgrade",
                release="ilum",
                namespace="default",
                context="",
                user="",
                chart_version="",
            )
        )

        entries = log.query()
        assert len(entries) == 2


class TestHistoryCommand:
    def test_history_no_entries(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        import sys

        if sys.platform == "win32":
            monkeypatch.setenv("APPDATA", str(tmp_path / "appdata"))
            monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "localappdata"))
        else:
            monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
            monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
            monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
            monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))

        runner = CliRunner()
        result = runner.invoke(app, ["history"])
        assert result.exit_code == 0
        assert "No history" in result.output

    def test_history_with_entries(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        import sys

        if sys.platform == "win32":
            monkeypatch.setenv("APPDATA", str(tmp_path / "appdata"))
            monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "localappdata"))
        else:
            monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
            monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
            monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
            monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))

        from ilum.config.paths import IlumPaths

        paths = IlumPaths.default()
        paths.ensure_dirs()

        log = AuditLog(paths.state_dir)
        log.append(
            AuditEntry(
                timestamp="2025-01-15T10:30:00",
                operation="install",
                release="ilum",
                namespace="default",
                context="minikube",
                user="testuser",
                chart_version="6.7.0",
                modules=["core", "ui", "mongodb"],
                success=True,
                duration_seconds=42.5,
            )
        )

        runner = CliRunner()
        result = runner.invoke(app, ["history"])
        assert result.exit_code == 0
        assert "install" in result.output
        assert "6.7.0" in result.output

    def test_history_json_output(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        import sys

        if sys.platform == "win32":
            monkeypatch.setenv("APPDATA", str(tmp_path / "appdata"))
            monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "localappdata"))
        else:
            monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
            monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
            monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
            monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))

        from ilum.config.paths import IlumPaths

        paths = IlumPaths.default()
        paths.ensure_dirs()

        log = AuditLog(paths.state_dir)
        log.append(
            AuditEntry(
                timestamp="2025-01-15T10:30:00",
                operation="install",
                release="ilum",
                namespace="default",
                context="minikube",
                user="test",
                chart_version="6.7.0",
            )
        )

        runner = CliRunner()
        result = runner.invoke(app, ["--output", "json", "history"])
        assert result.exit_code == 0
        data = json.loads(result.output)
        assert isinstance(data, list)
        assert data[0]["operation"] == "install"

    def test_history_last_n(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        import sys

        if sys.platform == "win32":
            monkeypatch.setenv("APPDATA", str(tmp_path / "appdata"))
            monkeypatch.setenv("LOCALAPPDATA", str(tmp_path / "localappdata"))
        else:
            monkeypatch.setenv("XDG_CONFIG_HOME", str(tmp_path / "config"))
            monkeypatch.setenv("XDG_DATA_HOME", str(tmp_path / "data"))
            monkeypatch.setenv("XDG_STATE_HOME", str(tmp_path / "state"))
            monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))

        from ilum.config.paths import IlumPaths

        paths = IlumPaths.default()
        paths.ensure_dirs()

        log = AuditLog(paths.state_dir)
        for i in range(5):
            log.append(
                AuditEntry(
                    timestamp=f"2025-01-{15 + i}T10:00:00",
                    operation="upgrade",
                    release="ilum",
                    namespace="default",
                    context="",
                    user="test",
                    chart_version="6.7.0",
                )
            )

        runner = CliRunner()
        result = runner.invoke(app, ["--output", "json", "history", "--last", "2"])
        assert result.exit_code == 0
        data = json.loads(result.output)
        assert len(data) == 2
