"""Tests for prompt.py: weighted strategy selection, review history, memory index."""

import json
import time
from pathlib import Path
from unittest.mock import patch

import pytest

import common
import prompt
import status


# ═══════════════════════════════════════════════════════════════════════════
# Weighted strategy selection (with journal data)
# ═══════════════════════════════════════════════════════════════════════════


class TestWeightedStrategySelection:
    def _seed_journal(self, project_dir: Path, entries: list):
        for e in entries:
            status.append_journal_entry(project_dir, e)

    def test_falls_back_to_round_robin_with_no_journal(self, project_dir: Path):
        s = prompt.pick_review_strategy(1, project_dir=project_dir)
        assert s == common.REVIEW_STRATEGIES[0]

    def test_falls_back_with_insufficient_reviews(self, project_dir: Path):
        self._seed_journal(project_dir, [
            {"iter": 1, "phases": ["review"], "strategy": "surface-sweep", "outcome": "productive"},
            {"iter": 2, "phases": ["review"], "strategy": "deep-dive", "outcome": "idle"},
        ])
        s = prompt.pick_review_strategy(3, project_dir=project_dir)
        expected_idx = (3 - 1) % len(common.REVIEW_STRATEGIES)
        assert s == common.REVIEW_STRATEGIES[expected_idx]

    def test_weighted_selection_favors_productive_strategy(self, project_dir: Path):
        entries = []
        for i in range(1, 11):
            entries.append({
                "iter": i, "phases": ["review"],
                "strategy": "deep-dive", "outcome": "productive",
            })
        for i in range(11, 21):
            entries.append({
                "iter": i, "phases": ["review"],
                "strategy": "surface-sweep", "outcome": "idle",
            })
        self._seed_journal(project_dir, entries)

        counts = {}
        with patch("prompt._random.random") as mock_rand:
            for trial_val in [x / 100 for x in range(100)]:
                mock_rand.return_value = trial_val
                s = prompt.pick_review_strategy(21, project_dir=project_dir)
                counts[s["name"]] = counts.get(s["name"], 0) + 1

        assert counts.get("deep-dive", 0) > counts.get("surface-sweep", 0)

    def test_ignores_non_review_entries(self, project_dir: Path):
        self._seed_journal(project_dir, [
            {"iter": 1, "phases": ["execute"], "strategy": "deep-dive", "outcome": "productive"},
            {"iter": 2, "phases": ["plan"], "outcome": "productive"},
        ])
        s = prompt.pick_review_strategy(3, project_dir=project_dir)
        expected_idx = (3 - 1) % len(common.REVIEW_STRATEGIES)
        assert s == common.REVIEW_STRATEGIES[expected_idx]

    def test_returns_valid_strategy(self, project_dir: Path):
        entries = [
            {"iter": i, "phases": ["review"], "strategy": "surface-sweep",
             "outcome": "productive" if i % 2 == 0 else "idle"}
            for i in range(1, 10)
        ]
        self._seed_journal(project_dir, entries)
        s = prompt.pick_review_strategy(10, project_dir=project_dir)
        names = {strat["name"] for strat in common.REVIEW_STRATEGIES}
        assert s["name"] in names


# ═══════════════════════════════════════════════════════════════════════════
# format_review_history
# ═══════════════════════════════════════════════════════════════════════════


class TestFormatReviewHistory:
    def test_empty_when_no_journal(self, project_dir: Path):
        assert prompt.format_review_history(project_dir) == ""

    def test_empty_when_no_review_entries(self, project_dir: Path):
        status.append_journal_entry(project_dir, {"iter": 1, "phases": ["execute"]})
        assert prompt.format_review_history(project_dir) == ""

    def test_shows_productive_with_count(self, project_dir: Path):
        status.append_journal_entry(project_dir, {
            "iter": 1, "phases": ["review"],
            "strategy": "deep-dive", "outcome": "productive",
            "created": ["task-a", "task-b"],
        })
        result = prompt.format_review_history(project_dir)
        assert "deep-dive→+2" in result
        assert result.startswith("RECENT REVIEWS:")

    def test_shows_idle(self, project_dir: Path):
        status.append_journal_entry(project_dir, {
            "iter": 1, "phases": ["review"],
            "strategy": "surface-sweep", "outcome": "idle",
        })
        result = prompt.format_review_history(project_dir)
        assert "surface-sweep→idle" in result

    def test_shows_failed(self, project_dir: Path):
        status.append_journal_entry(project_dir, {
            "iter": 1, "phases": ["review"],
            "strategy": "edge-cases", "outcome": "failed",
        })
        result = prompt.format_review_history(project_dir)
        assert "edge-cases→failed" in result

    def test_respects_max_show(self, project_dir: Path):
        for i in range(10):
            status.append_journal_entry(project_dir, {
                "iter": i + 1, "phases": ["review"],
                "strategy": f"strat-{i}", "outcome": "idle",
            })
        result = prompt.format_review_history(project_dir, max_show=3)
        assert result.count("→") == 3

    def test_multiple_entries_comma_separated(self, project_dir: Path):
        status.append_journal_entry(project_dir, {
            "iter": 1, "phases": ["review"],
            "strategy": "a", "outcome": "idle",
        })
        status.append_journal_entry(project_dir, {
            "iter": 2, "phases": ["review"],
            "strategy": "b", "outcome": "productive", "created": ["x"],
        })
        result = prompt.format_review_history(project_dir)
        assert ", " in result


# ═══════════════════════════════════════════════════════════════════════════
# _format_relative_time
# ═══════════════════════════════════════════════════════════════════════════


class TestFormatRelativeTime:
    def test_just_now(self):
        ts = int(time.time()) - 30
        assert prompt._format_relative_time(ts) == "just now"

    def test_minutes(self):
        ts = int(time.time()) - 300
        result = prompt._format_relative_time(ts)
        assert "min ago" in result

    def test_hours(self):
        ts = int(time.time()) - 7200
        result = prompt._format_relative_time(ts)
        assert "h ago" in result

    def test_days(self):
        ts = int(time.time()) - 172800
        result = prompt._format_relative_time(ts)
        assert "d ago" in result

    def test_zero_timestamp(self):
        result = prompt._format_relative_time(0)
        assert "d ago" in result


# ═══════════════════════════════════════════════════════════════════════════
# generate_memory_index (extended)
# ═══════════════════════════════════════════════════════════════════════════


class TestGenerateMemoryIndexExtended:
    def _setup(self, tmp_path: Path):
        project_dir = tmp_path
        (project_dir / ".autobuilder").mkdir(parents=True)
        init_base = project_dir / ".autobuilder" / "initiatives"
        tasks_dir = init_base / "0001-app" / "tasks"
        tasks_dir.mkdir(parents=True)
        return project_dir, tasks_dir, init_base

    def test_includes_strategy_effectiveness(self, tmp_path: Path):
        project_dir, tasks_dir, init_base = self._setup(tmp_path)
        for i in range(5):
            status.append_journal_entry(project_dir, {
                "iter": i + 1, "phases": ["review"],
                "strategy": "deep-dive",
                "outcome": "productive" if i < 3 else "idle",
                "ts": int(time.time()),
            })
        text = prompt.generate_memory_index(project_dir, tasks_dir, init_base, root_id="app")
        assert "Strategy Effectiveness" in text
        assert "deep-dive" in text
        assert "productive" in text

    def test_includes_recent_iterations(self, tmp_path: Path):
        project_dir, tasks_dir, init_base = self._setup(tmp_path)
        status.append_journal_entry(project_dir, {
            "iter": 1, "phases": ["review"], "strategy": "surface-sweep",
            "outcome": "idle", "ts": int(time.time()),
        })
        text = prompt.generate_memory_index(project_dir, tasks_dir, init_base, root_id="app")
        assert "Recent Iterations" in text
        assert "#1" in text

    def test_includes_pending_tasks(self, tmp_path: Path):
        project_dir, tasks_dir, init_base = self._setup(tmp_path)
        (tasks_dir / "0001-scaffold.md").write_text(
            "---\nid: app-scaffold\ntitle: Scaffold\nparent: app\ndeps: []\n"
            "split: false\ndepth: 1\nplanned: true\nexecuted: false\n---\n"
        )
        text = prompt.generate_memory_index(project_dir, tasks_dir, init_base, root_id="app")
        assert "Pending Tasks" in text

    def test_empty_journal_still_returns_header(self, tmp_path: Path):
        project_dir, tasks_dir, init_base = self._setup(tmp_path)
        text = prompt.generate_memory_index(project_dir, tasks_dir, init_base)
        assert "Iteration Memory" in text
