"""Tests for task management: numbering, querying, loading, ordering."""

import textwrap
from pathlib import Path

import pytest

import frontmatter
import tasks
from conftest import (
    SAMPLE_EXECUTED,
    SAMPLE_NO_EXECUTED_FIELD,
    SAMPLE_ROOT_TASK,
    SAMPLE_TASK,
    SAMPLE_UNPLANNED,
)


# ═══════════════════════════════════════════════════════════════════════════
# Numbering
# ═══════════════════════════════════════════════════════════════════════════


class TestMaxTaskNumber:
    def test_empty_dir(self, tmp_path: Path):
        assert tasks.max_task_number(tmp_path) == 0

    def test_nonexistent_dir(self, tmp_path: Path):
        assert tasks.max_task_number(tmp_path / "nope") == 0

    def test_finds_highest(self, tmp_path: Path):
        (tmp_path / "0001-a.md").write_text("")
        (tmp_path / "0010-b.md").write_text("")
        (tmp_path / "0005-c.md").write_text("")
        assert tasks.max_task_number(tmp_path) == 10


class TestNextTaskNumber:
    def test_empty(self, tmp_path: Path):
        assert tasks.next_task_number(tmp_path) == 1

    def test_increments(self, tmp_path: Path):
        (tmp_path / "0007-x.md").write_text("")
        assert tasks.next_task_number(tmp_path) == 8


class TestNumberedFilename:
    def test_basic(self):
        assert tasks.numbered_filename(1, "scaffold") == "0001-scaffold.md"
        assert tasks.numbered_filename(42, "charts") == "0042-charts.md"


class TestNormalizeTaskFilenames:
    def test_renames_unnumbered(self, tmp_path: Path):
        (tmp_path / "scaffold.md").write_text("")
        (tmp_path / "charts.md").write_text("")
        count = tasks.normalize_task_filenames(tmp_path)
        assert count == 2
        names = sorted(f.name for f in tmp_path.glob("*.md"))
        assert all(n[0:4].isdigit() for n in names)

    def test_leaves_numbered(self, tmp_path: Path):
        (tmp_path / "0001-scaffold.md").write_text("")
        count = tasks.normalize_task_filenames(tmp_path)
        assert count == 0

    def test_nonexistent_dir(self, tmp_path: Path):
        assert tasks.normalize_task_filenames(tmp_path / "nope") == 0


# ═══════════════════════════════════════════════════════════════════════════
# Querying
# ═══════════════════════════════════════════════════════════════════════════


class TestGetTaskIds:
    def test_empty(self, tasks_dir: Path):
        assert tasks.get_task_ids(tasks_dir) == {}

    def test_finds_ids(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        (tasks_dir / "0002-root.md").write_text(SAMPLE_ROOT_TASK)
        ids = tasks.get_task_ids(tasks_dir)
        assert "test-app-scaffold" in ids
        assert "test-app" in ids


class TestFindAllUnplanned:
    def test_empty(self, tasks_dir: Path):
        assert tasks.find_all_unplanned(tasks_dir) == []

    def test_finds_unplanned(self, tasks_dir: Path):
        (tasks_dir / "0001-charts.md").write_text(SAMPLE_UNPLANNED)
        result = tasks.find_all_unplanned(tasks_dir)
        assert len(result) == 1
        assert result[0][0] == "test-app-charts"

    def test_skips_planned(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        assert tasks.find_all_unplanned(tasks_dir) == []

    def test_skips_executed(self, tasks_dir: Path):
        (tasks_dir / "0001-done.md").write_text(SAMPLE_EXECUTED)
        assert tasks.find_all_unplanned(tasks_dir) == []

    def test_skips_split(self, tasks_dir: Path):
        (tasks_dir / "0001-root.md").write_text(SAMPLE_ROOT_TASK)
        assert tasks.find_all_unplanned(tasks_dir) == []


class TestFindPlannedNotExecuted:
    def test_empty(self, tasks_dir: Path):
        assert tasks.find_planned_not_executed(tasks_dir) == []

    def test_finds_planned_pending(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        result = tasks.find_planned_not_executed(tasks_dir)
        assert len(result) == 1
        assert result[0][0] == "test-app-scaffold"

    def test_skips_executed(self, tasks_dir: Path):
        (tasks_dir / "0001-done.md").write_text(SAMPLE_EXECUTED)
        assert tasks.find_planned_not_executed(tasks_dir) == []

    def test_skips_unplanned(self, tasks_dir: Path):
        (tasks_dir / "0001-charts.md").write_text(SAMPLE_UNPLANNED)
        assert tasks.find_planned_not_executed(tasks_dir) == []


class TestFindNewTasks:
    def test_detects_new(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        new = tasks.find_new_tasks(tasks_dir, set())
        assert len(new) == 1

    def test_excludes_known(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        new = tasks.find_new_tasks(
            tasks_dir, {"test-app-scaffold"}
        )
        assert len(new) == 0

    def test_excludes_executed(self, tasks_dir: Path):
        (tasks_dir / "0001-done.md").write_text(SAMPLE_EXECUTED)
        new = tasks.find_new_tasks(tasks_dir, set())
        assert len(new) == 0


class TestSummarizeExecutedTasks:
    def test_empty(self, tasks_dir: Path):
        assert tasks.summarize_executed_tasks(tasks_dir) == ""

    def test_shows_executed(self, tasks_dir: Path):
        (tasks_dir / "0001-done.md").write_text(SAMPLE_EXECUTED)
        summary = tasks.summarize_executed_tasks(tasks_dir)
        assert "test-app-done" in summary


class TestSummarizeAllExecutedTasks:
    def test_empty_base(self, tmp_path: Path):
        base = tmp_path / ".autobuilder" / "initiatives"
        base.mkdir(parents=True)
        assert tasks.summarize_all_executed_tasks(base) == ""

    def test_aggregates_across_initiatives(self, tmp_path: Path):
        init_base = tmp_path / ".autobuilder" / "initiatives"
        td1 = init_base / "0001-a" / "tasks"
        td2 = init_base / "0002-b" / "tasks"
        td1.mkdir(parents=True)
        td2.mkdir(parents=True)
        (td1 / "0001-one.md").write_text(SAMPLE_EXECUTED)
        other = SAMPLE_EXECUTED.replace("test-app-done", "other-done")
        (td2 / "0001-two.md").write_text(other)
        out = tasks.summarize_all_executed_tasks(init_base, root_id="test-app")
        assert "test-app-done" in out
        assert "other-done" in out


class TestExtractArea:
    def test_strips_root_prefix(self):
        assert tasks.extract_area("myapp-auth-login", "myapp") == "auth"

    def test_no_root_id(self):
        assert tasks.extract_area("foo-bar-baz", "") == "foo"

    def test_skips_leading_numeric_token(self):
        assert tasks.extract_area("myapp-2-auth", "myapp") == "auth"

    def test_empty_id_returns_empty_area_string(self):
        # Implementation: split yields [""]; first token is returned as-is.
        assert tasks.extract_area("", "") == ""


class TestGroupAllTasksByArea:
    def test_empty(self, tmp_path: Path):
        base = tmp_path / ".autobuilder" / "initiatives"
        base.mkdir(parents=True)
        assert tasks.group_all_tasks_by_area(base) == {}

    def test_groups_executed_across_initiatives(self, tmp_path: Path):
        init_base = tmp_path / ".autobuilder" / "initiatives"
        td = init_base / "0001-app" / "tasks"
        td.mkdir(parents=True)
        (td / "0001-t.md").write_text(SAMPLE_EXECUTED)
        groups = tasks.group_all_tasks_by_area(init_base, root_id="test-app")
        assert "done" in groups
        assert groups["done"]


# ═══════════════════════════════════════════════════════════════════════════
# Loading & ordering
# ═══════════════════════════════════════════════════════════════════════════


class TestLoadTasks:
    def test_loads_all(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        (tasks_dir / "0002-charts.md").write_text(SAMPLE_UNPLANNED)
        loaded = tasks.load_tasks(tasks_dir)
        assert len(loaded) == 2
        assert loaded["test-app-scaffold"]["planned"] is True
        assert loaded["test-app-charts"]["planned"] is False


class TestTopologicalSort:
    def test_simple_order(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        loaded = tasks.load_tasks(tasks_dir)
        order = tasks.topological_sort(loaded, set())
        assert order == ["test-app-scaffold"]

    def test_respects_deps(self, tasks_dir: Path):
        a = textwrap.dedent("""\
            ---
            id: a
            title: A
            deps: []
            split: false
            planned: true
            executed: false
            ---
        """)
        b = textwrap.dedent("""\
            ---
            id: b
            title: B
            deps: [a]
            split: false
            planned: true
            executed: false
            ---
        """)
        (tasks_dir / "0001-a.md").write_text(a)
        (tasks_dir / "0002-b.md").write_text(b)
        loaded = tasks.load_tasks(tasks_dir)
        order = tasks.topological_sort(loaded, set())
        assert order.index("a") < order.index("b")

    def test_cycle_raises(self, tasks_dir: Path):
        a = textwrap.dedent("""\
            ---
            id: a
            deps: [b]
            split: false
            planned: true
            executed: false
            ---
        """)
        b = textwrap.dedent("""\
            ---
            id: b
            deps: [a]
            split: false
            planned: true
            executed: false
            ---
        """)
        (tasks_dir / "0001-a.md").write_text(a)
        (tasks_dir / "0002-b.md").write_text(b)
        loaded = tasks.load_tasks(tasks_dir)
        with pytest.raises(ValueError, match="cycle"):
            tasks.topological_sort(loaded, set())

    def test_excludes_already_executed(self, tasks_dir: Path):
        (tasks_dir / "0001-scaffold.md").write_text(SAMPLE_TASK)
        loaded = tasks.load_tasks(tasks_dir)
        order = tasks.topological_sort(loaded, {"test-app-scaffold"})
        assert order == []


class TestMarkExecuted:
    def test_sets_executed_true(self, tmp_path: Path):
        f = tmp_path / "test.md"
        f.write_text(SAMPLE_TASK)
        tasks.mark_executed(f)
        fm = frontmatter.read_frontmatter(f)
        assert fm["executed"] == "true"

    def test_inserts_executed_when_missing(self, tmp_path: Path):
        f = tmp_path / "test.md"
        f.write_text(SAMPLE_NO_EXECUTED_FIELD)
        tasks.mark_executed(f)
        text = f.read_text()
        assert "executed: true" in text

    def test_idempotent(self, tmp_path: Path):
        f = tmp_path / "test.md"
        f.write_text(SAMPLE_EXECUTED)
        original = f.read_text()
        tasks.mark_executed(f)
        assert f.read_text() == original
