# SPDX-License-Identifier: AGPL-3.0-only
# Copyright (c) 2026 sol pbc

import argparse
import re
from datetime import datetime, timedelta

import pytest


def make_journal(tmp_path, day, services, supervisor_lines=None):
    """Create a synthetic journal with health logs."""
    health_dir = tmp_path / "chronicle" / day / "health"
    health_dir.mkdir(parents=True)

    for name, lines in services.items():
        actual = health_dir / f"ref_{name}.log"
        actual.write_text("\n".join(lines) + "\n" if lines else "", encoding="utf-8")

        symlink = health_dir / f"{name}.log"
        symlink.symlink_to(f"ref_{name}.log")

    journal_health = tmp_path / "health"
    journal_health.mkdir(exist_ok=True)

    for name in services:
        journal_sym = journal_health / f"{name}.log"
        journal_sym.symlink_to(f"../chronicle/{day}/health/ref_{name}.log")

    if supervisor_lines is not None:
        sup = journal_health / "supervisor.log"
        sup.write_text(
            "\n".join(supervisor_lines) + "\n" if supervisor_lines else "",
            encoding="utf-8",
        )

    return tmp_path


def _args(
    *,
    c: int = 5,
    since: datetime | None = None,
    service: str | None = None,
    grep: re.Pattern[str] | None = None,
) -> argparse.Namespace:
    return argparse.Namespace(c=c, f=False, since=since, service=service, grep=grep)


def test_parse_log_line_runner():
    from solstone.think.logs_cli import parse_log_line

    result = parse_log_line("2026-02-09T10:00:00 [echo:stdout] hello world")
    assert result is not None
    assert result.service == "echo"
    assert result.stream == "stdout"
    assert result.message == "hello world"
    assert result.timestamp == datetime(2026, 2, 9, 10, 0, 0)


def test_parse_log_line_supervisor():
    from solstone.think.logs_cli import parse_log_line

    result = parse_log_line(
        "2026-02-09T10:00:00 [supervisor:log] INFO Starting service"
    )
    assert result is not None
    assert result.service == "supervisor"
    assert result.stream == "log"
    assert result.message == "INFO Starting service"


def test_parse_log_line_malformed():
    from solstone.think.logs_cli import parse_log_line

    assert parse_log_line("not a log line") is None
    assert parse_log_line("") is None
    assert parse_log_line("2026-02-09T10:00:00 no brackets") is None


def test_parse_since_relative():
    from solstone.think.logs_cli import parse_since

    result = parse_since("30m")
    delta = datetime.now() - result
    assert timedelta(minutes=29) <= delta <= timedelta(minutes=31)


def test_parse_since_absolute():
    from solstone.think.logs_cli import parse_since

    result = parse_since("16:00")
    assert result.hour == 16
    assert result.minute == 0
    assert result.date() == datetime.now().date()


def test_parse_since_absolute_ampm():
    from solstone.think.logs_cli import parse_since

    result = parse_since("4pm")
    assert result.hour == 16
    result2 = parse_since("4:30pm")
    assert result2.hour == 16
    assert result2.minute == 30


def test_parse_since_invalid():
    from solstone.think.logs_cli import parse_since

    with pytest.raises(argparse.ArgumentTypeError):
        parse_since("xyz")


def test_filter_grep_invalid_regex():
    from solstone.think.logs_cli import compile_grep

    with pytest.raises(argparse.ArgumentTypeError):
        compile_grep("[invalid")


def test_tail_lines_large(tmp_path):
    from solstone.think.logs_cli import tail_lines_large

    path = tmp_path / "big.log"
    lines = [f"line {i}" for i in range(1000)]
    path.write_text("\n".join(lines) + "\n", encoding="utf-8")
    result = tail_lines_large(path, 5)
    assert result == [f"line {i}" for i in range(995, 1000)]


def test_get_day_log_files_filters_non_symlinks(tmp_path):
    from solstone.think.logs_cli import get_day_log_files

    health = tmp_path / "health"
    health.mkdir()
    (health / "ref_echo.log").write_text("data", encoding="utf-8")
    (health / "echo.log").symlink_to("ref_echo.log")
    (health / "something.port").write_text("8080", encoding="utf-8")
    result = get_day_log_files(health)
    assert len(result) == 1
    assert result[0].name == "echo.log"


def test_collect_default(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [f"2026-02-09T10:{i:02d}:00 [echo:stdout] line {i}" for i in range(10)]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(c=5))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 5
    assert "line 5" in output[0]
    assert "line 9" in output[4]


def test_collect_count(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [f"2026-02-09T11:{i:02d}:00 [echo:stdout] line {i}" for i in range(10)]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(c=2))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 2
    assert "line 8" in output[0]
    assert "line 9" in output[1]


def test_collect_headers_on_tty(tmp_path, monkeypatch, capsys):
    """Service headers appear when stdout is a TTY."""
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] line a",
        "2026-02-09T10:02:00 [echo:stdout] line c",
    ]
    observer_lines = [
        "2026-02-09T10:01:00 [observer:stdout] line b",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines, "observer": observer_lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))
    monkeypatch.setattr("sys.stdout.isatty", lambda: True)

    logs_cli.collect_and_print(_args(c=5))

    raw = capsys.readouterr().out
    output = raw.strip().splitlines()
    # Should have: header + line a, blank + header + line b, blank + header + line c
    # That's 3 headers + 3 content lines + 2 blank lines = 8 lines total
    assert any("── echo ──" in line for line in output)
    assert any("── observer ──" in line for line in output)
    # Content lines are still present
    assert any("line a" in line for line in output)
    assert any("line b" in line for line in output)
    assert any("line c" in line for line in output)


def test_collect_no_headers_when_piped(tmp_path, monkeypatch, capsys):
    """No headers when stdout is not a TTY (piped)."""
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] line a",
    ]
    observer_lines = [
        "2026-02-09T10:01:00 [observer:stdout] line b",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines, "observer": observer_lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))
    monkeypatch.setattr("sys.stdout.isatty", lambda: False)

    logs_cli.collect_and_print(_args(c=5))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 2
    assert not any("──" in line for line in output)


def test_filter_service(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] alpha",
        "2026-02-09T10:01:00 [echo:stdout] beta",
    ]
    observer_lines = [
        "2026-02-09T10:00:30 [observer:stdout] gamma",
        "2026-02-09T10:01:30 [observer:stdout] delta",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines, "observer": observer_lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(service="echo"))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 2
    assert all("[echo:stdout]" in line for line in output)


def test_filter_grep(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [
        "2026-02-09T10:00:00 [echo:stdout] normal line",
        "2026-02-09T10:01:00 [echo:stdout] special event",
        "2026-02-09T10:02:00 [echo:stdout] unrelated text",
    ]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(grep=re.compile("normal|special")))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 2
    assert "normal line" in output[0]
    assert "special event" in output[1]


def test_filter_grep_regex_or(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [
        "2026-02-09T10:00:00 [echo:stdout] alpha entry",
        "2026-02-09T10:01:00 [echo:stdout] special entry",
        "2026-02-09T10:02:00 [echo:stdout] beta entry",
    ]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(grep=re.compile("alpha|special")))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 2
    assert "alpha entry" in output[0]
    assert "special entry" in output[1]


def test_filter_since(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [
        "2026-02-09T10:00:00 [echo:stdout] old",
        "2026-02-09T10:20:00 [echo:stdout] new",
    ]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(since=datetime(2026, 2, 9, 10, 10, 0)))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 1
    assert "new" in output[0]


def test_count_limits_filtered_output(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    lines = [
        f"2026-02-09T10:{i:02d}:00 [echo:stdout] special line {i}" for i in range(10)
    ]
    make_journal(tmp_path, day, {"echo": lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(c=3, grep=re.compile("special")))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 3
    assert "special line 7" in output[0]
    assert "special line 8" in output[1]
    assert "special line 9" in output[2]


def test_filters_compose(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] keep this special",
        "2026-02-09T10:01:00 [echo:stdout] ignore this",
    ]
    observer_lines = [
        "2026-02-09T10:00:30 [observer:stdout] special but wrong service",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines, "observer": observer_lines})
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(service="echo", grep=re.compile("special")))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 1
    assert "[echo:stdout]" in output[0]
    assert "keep this special" in output[0]


def test_supervisor_included_default(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] line 0",
        "2026-02-09T10:01:00 [echo:stdout] line 1",
        "2026-02-09T10:02:00 [echo:stdout] line 2",
    ]
    supervisor_lines = [
        "2026-02-09T10:03:00 [supervisor:log] INFO a",
        "2026-02-09T10:04:00 [supervisor:log] INFO b",
        "2026-02-09T10:05:00 [supervisor:log] INFO c",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines}, supervisor_lines=supervisor_lines)
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(c=2))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 4
    assert any("[echo:stdout]" in line for line in output)
    assert any("[supervisor:log]" in line for line in output)


def test_supervisor_excluded_with_filters(tmp_path, monkeypatch, capsys):
    from solstone.think import logs_cli

    day = datetime.now().strftime("%Y%m%d")
    echo_lines = [
        "2026-02-09T10:00:00 [echo:stdout] special",
    ]
    supervisor_lines = [
        "2026-02-09T10:01:00 [supervisor:log] INFO special",
    ]
    make_journal(tmp_path, day, {"echo": echo_lines}, supervisor_lines=supervisor_lines)
    monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path))

    logs_cli.collect_and_print(_args(grep=re.compile("special")))

    output = capsys.readouterr().out.strip().splitlines()
    assert len(output) == 1
    assert "[echo:stdout]" in output[0]
    assert "[supervisor:log]" not in output[0]
