"""Tests for log command helpers."""

import os
import time
from pathlib import Path

import pytest

import datetime

from scry_run.cli.log import (
    _find_offset_for_since,
    _iter_recent_logs,
    _color_for_name,
    _parse_duration,
    _parse_since,
    _read_new_data,
    _TailState,
    _build_colored_lines,
    _wait_for_log_dir,
)


def test_parse_duration_seconds() -> None:
    assert _parse_duration("10") == 10.0
    assert _parse_duration("2s") == 2.0


def test_parse_duration_minutes_hours() -> None:
    assert _parse_duration("1m") == 60.0
    assert _parse_duration("1.5h") == 5400.0


def test_parse_duration_invalid() -> None:
    with pytest.raises(ValueError):
        _parse_duration("")
    with pytest.raises(ValueError):
        _parse_duration("1d")
    with pytest.raises(ValueError):
        _parse_duration("abc")


def test_parse_since_duration() -> None:
    now = datetime.datetime(2026, 2, 4, 12, 0, 0)
    assert _parse_since("30m", now) == datetime.datetime(2026, 2, 4, 11, 30, 0)


def test_parse_since_iso() -> None:
    now = datetime.datetime(2026, 2, 4, 12, 0, 0)
    assert _parse_since("2026-02-04T11:30:00", now) == datetime.datetime(2026, 2, 4, 11, 30, 0)


def test_read_new_data_buffers_partial_line(tmp_path: Path) -> None:
    path = tmp_path / "test.log"
    path.write_text("hello")
    state = _TailState(path=path, position=0, buffer="")

    assert _read_new_data(state) == []
    assert state.buffer == "hello"

    with path.open("a", encoding="utf-8") as f:
        f.write("\nworld\n")

    lines = _read_new_data(state)
    assert lines == ["hello\n", "world\n"]
    assert state.buffer == ""


def test_read_new_data_handles_truncate(tmp_path: Path) -> None:
    path = tmp_path / "test.log"
    path.write_text("one\n")
    state = _TailState(path=path, position=0, buffer="")
    _read_new_data(state)

    # Truncate and write new content
    path.write_text("")
    with path.open("a", encoding="utf-8") as f:
        f.write("x\n")

    lines = _read_new_data(state)
    assert lines == ["x\n"]


def test_iter_recent_logs_filters_by_mtime(tmp_path: Path) -> None:
    recent = tmp_path / "recent.log"
    old = tmp_path / "old.log"
    recent.write_text("a")
    old.write_text("b")

    now = time.time()
    os.utime(recent, (now, now))
    os.utime(old, (now - 120, now - 120))

    paths = _iter_recent_logs(tmp_path, since_seconds=60)
    assert recent in paths
    assert old not in paths


def test_find_offset_for_since(tmp_path: Path) -> None:
    path = tmp_path / "test.log"
    path.write_text(
        "\n"
        + "=" * 80
        + "\n"
        + "[2026-02-04T10:00:00] [INFO] first\n"
        + "-" * 40
        + "\n"
        + "detail\n"
        + "\n"
        + "=" * 80
        + "\n"
        + "[2026-02-04T12:00:00] [INFO] second\n"
    )

    cutoff = datetime.datetime(2026, 2, 4, 11, 0, 0)
    offset = _find_offset_for_since(path, cutoff)
    with path.open("r", encoding="utf-8") as f:
        f.seek(offset)
        chunk = f.read()
    assert "[2026-02-04T12:00:00] [INFO] second" in chunk


def test_build_colored_lines_applies_styles() -> None:
    lines = [
        "[2026-02-04T12:00:00] [ERROR] boom\n",
        "plain line\n",
        "=" * 80 + "\n",
    ]
    path = Path("timer.log")
    text = _build_colored_lines(path, lines)
    plain = text.plain

    prefix = f"[{path.name}] "
    assert plain.count(prefix) == 3

    spans = [(span.style, plain[span.start:span.end]) for span in text.spans]
    expected_prefix_style = _color_for_name(path.name)
    assert sum(1 for style, segment in spans if style == expected_prefix_style and segment == prefix) == 3
    assert any(style == "red" and segment == "[ERROR] " for style, segment in spans)
    assert any(style == "dim" and segment == "[2026-02-04T12:00:00] " for style, segment in spans)
    assert any(style == "dim" and segment.strip() == "=" * 80 for style, segment in spans)


def test_build_colored_lines_short_mode_prefix() -> None:
    lines = ["plain line\n"]
    path = Path("timer.log")
    text = _build_colored_lines(path, lines, short_mode=True)

    assert text.plain == "> plain line\n"
    spans = [(span.style, text.plain[span.start:span.end]) for span in text.spans]
    assert any(style == "dim" and segment == "> " for style, segment in spans)


def test_build_colored_lines_short_mode_hides_timestamp() -> None:
    lines = ["[2026-02-04T12:00:00] [DEBUG] query (1839 chars)\n"]
    path = Path("timer.log")
    text = _build_colored_lines(path, lines, short_mode=True)

    assert text.plain == "> [DEBUG] query (1839 chars)\n"
    assert "[2026-02-04T12:00:00]" not in text.plain


def test_wait_for_log_dir_immediate_when_exists(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("SCRY_HOME", str(tmp_path))
    log_dir = tmp_path / "apps" / "timer" / "logs"
    log_dir.mkdir(parents=True)

    assert _wait_for_log_dir("timer", poll_interval=0.01, max_wait_seconds=0.1) == log_dir


def test_wait_for_log_dir_times_out_when_missing(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setenv("SCRY_HOME", str(tmp_path))

    with pytest.raises(TimeoutError):
        _wait_for_log_dir("missing", poll_interval=0.01, max_wait_seconds=0.03)
