#!/usr/bin/env python3
"""task-80.1 테스트: task-timer.py log 구조화 + anu-actions.log 이중기록

Tests the extended `log` command:
  - New parameters: --actor, --file, --action
  - Dual logging to anu-actions.log
  - Backward compatibility with old format
"""

import json
import os
import re
import subprocess
import sys
from datetime import datetime
from pathlib import Path

TIMER_SCRIPT = "/home/jay/workspace/memory/task-timer.py"
DAILY_DIR = "/home/jay/workspace/memory/daily"
ANU_ACTIONS_LOG = os.path.join(DAILY_DIR, "anu-actions.log")

# --------------------------------------------------------------------------- #
# Helpers
# --------------------------------------------------------------------------- #

def run_log_cmd(args_list):
    """Run task-timer.py log with given args.

    Returns (returncode, stdout, stderr).
    """
    cmd = [sys.executable, TIMER_SCRIPT, "log"] + args_list
    result = subprocess.run(cmd, capture_output=True, text=True)
    return result.returncode, result.stdout, result.stderr


def get_daily_log_path():
    """Return the path for today's daily markdown log."""
    date_str = datetime.now().strftime("%Y-%m-%d")
    return os.path.join(DAILY_DIR, f"{date_str}.md")


def read_file_lines(path):
    """Read all lines from a file; return [] if file does not exist."""
    try:
        with open(path, "r", encoding="utf-8") as f:
            return f.readlines()
    except FileNotFoundError:
        return []


def last_n_lines(path, n):
    """Return the last n lines of a file as a list of stripped strings."""
    lines = read_file_lines(path)
    return [l.rstrip("\n") for l in lines[-n:]]


# --------------------------------------------------------------------------- #
# Test state
# --------------------------------------------------------------------------- #

_results = []          # list of (test_name, passed, message)
_backup_content = None  # content of anu-actions.log before tests ran
_backup_existed = False


def _backup_anu_actions():
    """Back up anu-actions.log content (or note it did not exist)."""
    global _backup_content, _backup_existed
    if os.path.exists(ANU_ACTIONS_LOG):
        _backup_existed = True
        with open(ANU_ACTIONS_LOG, "r", encoding="utf-8") as f:
            _backup_content = f.read()
    else:
        _backup_existed = False
        _backup_content = None


def _restore_anu_actions():
    """Restore anu-actions.log to its pre-test state."""
    if _backup_existed:
        with open(ANU_ACTIONS_LOG, "w", encoding="utf-8") as f:
            f.write(_backup_content)
    else:
        if os.path.exists(ANU_ACTIONS_LOG):
            os.remove(ANU_ACTIONS_LOG)


def record(name, passed, message=""):
    """Record a test result and print immediate feedback."""
    _results.append((name, passed, message))
    status = "PASS" if passed else "FAIL"
    if message and not passed:
        print(f"  [{status}] {name}")
        print(f"         Reason: {message}")
    else:
        print(f"  [{status}] {name}")


def assert_true(condition, message):
    """Raise AssertionError with message if condition is False."""
    if not condition:
        raise AssertionError(message)


# --------------------------------------------------------------------------- #
# Individual tests
# --------------------------------------------------------------------------- #

def test_01_basic_backward_compat():
    """Test 1: Basic backward compatibility - old format without new flags."""
    name = "Test 1: 기본 하위 호환성 (old format)"
    try:
        rc, stdout, stderr = run_log_cmd(["기존 형식 테스트"])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        try:
            data = json.loads(stdout)
        except json.JSONDecodeError as e:
            raise AssertionError(f"stdout is not valid JSON: {e}\nstdout={stdout!r}")

        assert_true(data.get("status") == "logged",
            f"Expected status='logged', got {data!r}")

        # Check that daily log contains the message with defaults
        daily_lines = read_file_lines(get_daily_log_path())
        matching = [l for l in daily_lines if "기존 형식 테스트" in l]
        assert_true(len(matching) > 0,
            "Daily log should contain '기존 형식 테스트' but it was not found.")

        # Defaults: actor=anu, file=-, action=note
        # The line must contain [anu], [-], [note] OR follow whatever default
        # the new implementation chooses.  We verify the message is present and
        # the three default tokens appear together somewhere on the matching line.
        line = matching[-1]
        assert_true("[anu]" in line,
            f"Daily log line should contain '[anu]' (default actor). Line: {line!r}")
        assert_true("[-]" in line,
            f"Daily log line should contain '[-]' (default file). Line: {line!r}")
        assert_true("[note]" in line,
            f"Daily log line should contain '[note]' (default action). Line: {line!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_02_full_structured_log():
    """Test 2: Full structured log with --actor, --file, --action."""
    name = "Test 2: 전체 구조화 로그 (--actor --file --action)"
    try:
        rc, stdout, stderr = run_log_cmd([
            "한정 위임 규칙 추가",
            "--actor", "anu",
            "--file", "CLAUDE.md",
            "--action", "edit",
        ])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        try:
            data = json.loads(stdout)
        except json.JSONDecodeError as e:
            raise AssertionError(f"stdout is not valid JSON: {e}\nstdout={stdout!r}")

        assert_true(data.get("status") == "logged",
            f"Expected status='logged', got {data!r}")

        daily_lines = read_file_lines(get_daily_log_path())
        matching = [l for l in daily_lines if "한정 위임 규칙 추가" in l]
        assert_true(len(matching) > 0,
            "Daily log should contain '한정 위임 규칙 추가' but it was not found.")

        line = matching[-1]
        assert_true("[anu]" in line,
            f"Daily log line should contain '[anu]'. Line: {line!r}")
        assert_true("[CLAUDE.md]" in line,
            f"Daily log line should contain '[CLAUDE.md]'. Line: {line!r}")
        assert_true("[edit]" in line,
            f"Daily log line should contain '[edit]'. Line: {line!r}")
        assert_true("한정 위임 규칙 추가" in line,
            f"Daily log line should contain the message. Line: {line!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_03_partial_params_only_action():
    """Test 3: Partial parameters - only --action provided."""
    name = "Test 3: 부분 파라미터 (--action 만)"
    try:
        rc, stdout, stderr = run_log_cmd(["리뷰 완료", "--action", "review"])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        try:
            data = json.loads(stdout)
        except json.JSONDecodeError as e:
            raise AssertionError(f"stdout is not valid JSON: {e}\nstdout={stdout!r}")

        assert_true(data.get("status") == "logged",
            f"Expected status='logged', got {data!r}")

        daily_lines = read_file_lines(get_daily_log_path())
        matching = [l for l in daily_lines if "리뷰 완료" in l]
        assert_true(len(matching) > 0,
            "Daily log should contain '리뷰 완료'.")

        line = matching[-1]
        # actor defaults to anu, file defaults to -
        assert_true("[anu]" in line,
            f"Default actor '[anu]' expected. Line: {line!r}")
        assert_true("[-]" in line,
            f"Default file '[-]' expected. Line: {line!r}")
        assert_true("[review]" in line,
            f"Explicit action '[review]' expected. Line: {line!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_04_anu_actions_log():
    """Test 4: anu-actions.log creation and format after any log call."""
    name = "Test 4: anu-actions.log 생성 및 형식"
    try:
        # Trigger a log entry that is easily identifiable
        marker = "anu-actions-log-형식-검증"
        rc, stdout, stderr = run_log_cmd([
            marker,
            "--actor", "anu",
            "--action", "test",
            "--file", "test.py",
        ])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        assert_true(os.path.exists(ANU_ACTIONS_LOG),
            f"anu-actions.log should exist at {ANU_ACTIONS_LOG}")

        lines = read_file_lines(ANU_ACTIONS_LOG)
        matching = [l.rstrip("\n") for l in lines if marker in l]
        assert_true(len(matching) > 0,
            f"anu-actions.log should contain marker '{marker}'.")

        line = matching[-1]
        # Expected format: [YYYY-MM-DD HH:MM:SS] [actor] [action] [file] message
        # Validate the timestamp bracket at the start
        assert_true(re.match(r"^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\]", line),
            f"Line should start with a timestamp [YYYY-MM-DD HH:MM:SS]. Line: {line!r}")
        assert_true("[anu]" in line,
            f"anu-actions.log line should contain '[anu]'. Line: {line!r}")
        assert_true("[test]" in line,
            f"anu-actions.log line should contain '[test]' (action). Line: {line!r}")
        assert_true("[test.py]" in line,
            f"anu-actions.log line should contain '[test.py]' (file). Line: {line!r}")
        assert_true(marker in line,
            f"anu-actions.log line should contain the message. Line: {line!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_05_type_flag_still_works():
    """Test 5: Existing --type flag routes to the correct section."""
    name = "Test 5: 기존 --type 플래그 호환성"
    try:
        marker = "결정-사항-테스트-마커"
        rc, stdout, stderr = run_log_cmd([
            marker,
            "--type", "decision",
            "--actor", "anu",
            "--action", "decision",
        ])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        try:
            data = json.loads(stdout)
        except json.JSONDecodeError as e:
            raise AssertionError(f"stdout is not valid JSON: {e}\nstdout={stdout!r}")

        assert_true(data.get("status") == "logged",
            f"Expected status='logged', got {data!r}")

        # The entry must appear in the daily log
        daily_content = "".join(read_file_lines(get_daily_log_path()))
        assert_true(marker in daily_content,
            f"Daily log should contain the decision marker '{marker}'.")

        # It must be placed under the '의사결정' section
        # Find the section header position and verify the marker comes after it
        decision_section_pos = daily_content.find("## 의사결정")
        marker_pos = daily_content.rfind(marker)  # last occurrence

        assert_true(decision_section_pos != -1,
            "Daily log should have '## 의사결정' section after --type decision.")
        assert_true(marker_pos > decision_section_pos,
            f"Marker should appear after '## 의사결정' section. "
            f"section_pos={decision_section_pos}, marker_pos={marker_pos}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_06_multiple_sequential_logs_accumulate():
    """Test 6: Multiple sequential log calls accumulate lines in anu-actions.log."""
    name = "Test 6: 순차적 로그 누적 (anu-actions.log)"
    try:
        # Count current lines before the batch
        lines_before = read_file_lines(ANU_ACTIONS_LOG) if os.path.exists(ANU_ACTIONS_LOG) else []
        count_before = len(lines_before)

        messages = [
            ("첫 번째 누적 테스트", "read"),
            ("두 번째 누적 테스트", "write"),
            ("세 번째 누적 테스트", "verify"),
        ]

        for msg, action in messages:
            rc, stdout, stderr = run_log_cmd([msg, "--action", action])
            assert_true(rc == 0,
                f"log call failed for '{msg}': rc={rc}, stderr={stderr!r}")

        assert_true(os.path.exists(ANU_ACTIONS_LOG),
            f"anu-actions.log should exist at {ANU_ACTIONS_LOG}")

        lines_after = read_file_lines(ANU_ACTIONS_LOG)
        count_after = len(lines_after)

        assert_true(count_after >= count_before + len(messages),
            f"anu-actions.log should have grown by at least {len(messages)} lines. "
            f"Before={count_before}, After={count_after}")

        # All three messages must appear in anu-actions.log
        content_after = "".join(lines_after)
        for msg, _ in messages:
            assert_true(msg in content_after,
                f"anu-actions.log should contain '{msg}' after sequential calls.")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_07_json_output_contains_actor_file_action():
    """Test 7: JSON output reflects actor, file, action fields."""
    name = "Test 7: JSON 출력에 actor/file/action 필드 포함"
    try:
        rc, stdout, stderr = run_log_cmd([
            "JSON 필드 검증",
            "--actor", "dev1",
            "--file", "README.md",
            "--action", "create",
        ])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")

        try:
            data = json.loads(stdout)
        except json.JSONDecodeError as e:
            raise AssertionError(f"stdout is not valid JSON: {e}\nstdout={stdout!r}")

        assert_true(data.get("status") == "logged",
            f"Expected status='logged', got {data!r}")
        assert_true(data.get("actor") == "dev1",
            f"Expected actor='dev1' in JSON output, got {data!r}")
        assert_true(data.get("file") == "README.md",
            f"Expected file='README.md' in JSON output, got {data!r}")
        assert_true(data.get("action") == "create",
            f"Expected action='create' in JSON output, got {data!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_08_missing_message_exits_nonzero():
    """Test 8: Calling log with no message should exit with non-zero code."""
    name = "Test 8: 메시지 없이 호출 시 오류 종료"
    try:
        rc, stdout, stderr = run_log_cmd([])
        assert_true(rc != 0,
            f"Expected non-zero exit code when message is missing, got {rc}.")
        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_09_invalid_type_exits_nonzero():
    """Test 9: Invalid --type value should exit with non-zero code."""
    name = "Test 9: 잘못된 --type 값 시 오류 종료"
    try:
        rc, stdout, stderr = run_log_cmd(["테스트", "--type", "invalid_xyz"])
        assert_true(rc != 0,
            f"Expected non-zero exit code for invalid --type, got {rc}.")
        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


def test_10_anu_actions_log_format_no_actor_file_flags():
    """Test 10: anu-actions.log uses defaults even when no --actor/--file given."""
    name = "Test 10: anu-actions.log 기본값 (플래그 없음)"
    try:
        marker = "기본값-anu-actions-검증"
        rc, stdout, stderr = run_log_cmd([marker])

        assert_true(rc == 0,
            f"Exit code should be 0, got {rc}. stderr={stderr!r}")
        assert_true(os.path.exists(ANU_ACTIONS_LOG),
            f"anu-actions.log should exist at {ANU_ACTIONS_LOG}")

        lines = read_file_lines(ANU_ACTIONS_LOG)
        matching = [l.rstrip("\n") for l in lines if marker in l]
        assert_true(len(matching) > 0,
            f"anu-actions.log should contain marker '{marker}'.")

        line = matching[-1]
        # All three bracket groups must be present with defaults
        assert_true("[anu]" in line,
            f"Default actor '[anu]' expected in anu-actions.log. Line: {line!r}")
        assert_true("[-]" in line,
            f"Default file '[-]' expected in anu-actions.log. Line: {line!r}")
        assert_true("[note]" in line,
            f"Default action '[note]' expected in anu-actions.log. Line: {line!r}")

        record(name, True)
    except AssertionError as e:
        record(name, False, str(e))


# --------------------------------------------------------------------------- #
# Runner
# --------------------------------------------------------------------------- #

def main():
    print("=" * 60)
    print("task-80.1 테스트: task-timer.py log 구조화 + anu-actions.log")
    print("=" * 60)
    print(f"TIMER_SCRIPT  : {TIMER_SCRIPT}")
    print(f"DAILY_DIR     : {DAILY_DIR}")
    print(f"ANU_ACTIONS   : {ANU_ACTIONS_LOG}")
    print(f"TODAY LOG     : {get_daily_log_path()}")
    print()

    # Pre-flight: script must exist
    if not os.path.exists(TIMER_SCRIPT):
        print(f"FATAL: task-timer.py not found at {TIMER_SCRIPT}")
        sys.exit(2)

    # Back up anu-actions.log so tests are idempotent
    _backup_anu_actions()

    try:
        print("-- Running tests --")
        test_01_basic_backward_compat()
        test_02_full_structured_log()
        test_03_partial_params_only_action()
        test_04_anu_actions_log()
        test_05_type_flag_still_works()
        test_06_multiple_sequential_logs_accumulate()
        test_07_json_output_contains_actor_file_action()
        test_08_missing_message_exits_nonzero()
        test_09_invalid_type_exits_nonzero()
        test_10_anu_actions_log_format_no_actor_file_flags()
    finally:
        # Always restore anu-actions.log regardless of test outcome
        _restore_anu_actions()

    print()
    print("=" * 60)
    passed = [r for r in _results if r[1]]
    failed = [r for r in _results if not r[1]]
    total = len(_results)
    print(f"Summary: {len(passed)}/{total} passed, {len(failed)}/{total} failed")
    if failed:
        print()
        print("Failed tests:")
        for name, _, msg in failed:
            print(f"  - {name}")
            if msg:
                print(f"    {msg}")
    print("=" * 60)

    sys.exit(0 if not failed else 1)


if __name__ == "__main__":
    main()
