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

import importlib
import os
import uuid
from pathlib import Path

import pytest


def test_get_talent_configs_generators():
    """Test that system generators are discovered with source field."""
    talent = importlib.import_module("solstone.think.talent")
    generators = talent.get_talent_configs(type="generate")
    assert "schedule" in generators
    info = generators["schedule"]
    assert os.path.basename(info["path"]) == "schedule.md"
    assert isinstance(info["color"], str)
    assert isinstance(info["mtime"], int)
    assert "title" in info
    assert "occurrences" not in info
    # New: check source field
    assert info.get("source") == "system"


def test_get_output_name():
    """Test generator key to filename conversion."""
    talent = importlib.import_module("solstone.think.talent")

    # System generators: key unchanged
    assert talent.get_output_name("activity") == "activity"
    assert talent.get_output_name("flow") == "flow"

    # App generators: _app_name format
    assert talent.get_output_name("chat:sentiment") == "_chat_sentiment"
    assert talent.get_output_name("my_app:weekly_summary") == "_my_app_weekly_summary"


def test_get_talent_configs_app_discovery(tmp_path, monkeypatch):
    """Test that app generators are discovered from apps/*/talent/."""
    talent = importlib.import_module("solstone.think.talent")

    # Create a fake app with a generator
    app_dir = tmp_path / "apps" / "test_app" / "talent"
    app_dir.mkdir(parents=True)

    # Create generator files with frontmatter
    (app_dir / "custom_generator.md").write_text(
        '{\n  "title": "Custom Generator",\n  "color": "#ff0000"\n}\n\nTest prompt'
    )

    # Also create workspace.html to make it a valid app (not strictly required for generators)
    (tmp_path / "apps" / "test_app" / "workspace.html").write_text("<h1>Test</h1>")

    # For now, just verify system generators have correct source
    generators = talent.get_talent_configs(type="generate")
    for key, info in generators.items():
        if ":" not in key:
            assert info.get("source") == "system", f"{key} should have source=system"


def test_get_talent_configs_by_schedule():
    """Test filtering generators by schedule."""
    talent = importlib.import_module("solstone.think.talent")

    # Get daily generators
    daily = talent.get_talent_configs(type="generate", schedule="daily")
    assert len(daily) > 0
    for key, meta in daily.items():
        assert meta.get("schedule") == "daily", f"{key} should have schedule=daily"

    # Get segment generators
    segment = talent.get_talent_configs(type="generate", schedule="segment")
    assert len(segment) > 0
    for key, meta in segment.items():
        assert meta.get("schedule") == "segment", f"{key} should have schedule=segment"

    # Verify no overlap
    assert not set(daily.keys()) & set(segment.keys()), (
        "daily and segment should not overlap"
    )

    # Unknown schedule returns empty dict
    assert talent.get_talent_configs(type="generate", schedule="hourly") == {}
    assert talent.get_talent_configs(type="generate", schedule="") == {}


def test_get_talent_configs_include_disabled(monkeypatch):
    """Test include_disabled parameter."""
    talent = importlib.import_module("solstone.think.talent")

    # Get generators without disabled (default)
    without_disabled = talent.get_talent_configs(type="generate", schedule="daily")

    # Get generators with disabled included
    with_disabled = talent.get_talent_configs(
        type="generate", schedule="daily", include_disabled=True
    )

    # Should have at least as many with disabled included
    assert len(with_disabled) >= len(without_disabled)


def test_scheduled_generators_have_valid_schedule():
    """Test that scheduled generators have valid schedule field.

    Generators with a schedule field must have valid values
    ('segment', 'daily', 'weekly', 'cadence', or 'activity'). Some generators
    (like importer) have output but no schedule - they're used for ad-hoc
    processing, not scheduled runs.
    """
    talent = importlib.import_module("solstone.think.talent")

    generators = talent.get_talent_configs(type="generate")
    valid_schedules = ("segment", "daily", "activity", "weekly", "cadence")

    for key, meta in generators.items():
        sched = meta.get("schedule")
        if sched is not None:
            assert sched in valid_schedules, (
                f"Generator '{key}' has invalid schedule '{sched}'"
            )


def test_all_talent_configs_declare_non_empty_title():
    talent = importlib.import_module("solstone.think.talent")

    configs = talent.get_talent_configs(include_disabled=True)
    missing = [
        key
        for key, meta in configs.items()
        if not isinstance(meta.get("title"), str) or not meta["title"].strip()
    ]

    assert not missing, f"talent configs missing non-empty title: {missing}"


def test_sense_in_segment_schedule():
    """Test that sense generator exists in segment schedule at priority 5."""
    talent = importlib.import_module("solstone.think.talent")

    generators = talent.get_talent_configs(type="generate", schedule="segment")
    assert "sense" in generators

    sense = generators["sense"]
    assert sense.get("priority") == 5, "sense should be at priority 5"

    sources = sense.get("load", {})

    assert sources.get("transcripts") is True, "sense should include transcripts"
    assert sources.get("percepts") is True, "sense should include percepts"


def _write_temp_talent_prompt(talent_dir: Path, stem: str, frontmatter: str) -> Path:
    talent_dir.mkdir(parents=True, exist_ok=True)
    prompt_path = talent_dir / f"{stem}.md"
    prompt_path.write_text(
        f"{frontmatter}\n\nTemporary test prompt\n", encoding="utf-8"
    )
    return prompt_path


def test_get_talent_configs_raises_on_missing_type_with_output(tmp_path, monkeypatch):
    talent = importlib.import_module("solstone.think.talent")
    monkeypatch.setattr(talent, "TALENT_DIR", tmp_path)
    stem = f"test_missing_type_output_{uuid.uuid4().hex}"
    prompt_path = _write_temp_talent_prompt(
        tmp_path,
        stem,
        '{\n  "schedule": "daily",\n  "priority": 10,\n  "output": "md"\n}',
    )
    try:
        with pytest.raises(
            ValueError, match=rf"Prompt '{stem}'.*missing required 'type'"
        ):
            talent.get_talent_configs(include_disabled=True)
    finally:
        prompt_path.unlink(missing_ok=True)


def test_get_talent_configs_allows_missing_type_with_tools(tmp_path, monkeypatch):
    talent = importlib.import_module("solstone.think.talent")
    monkeypatch.setattr(talent, "TALENT_DIR", tmp_path)
    stem = f"test_missing_type_tools_{uuid.uuid4().hex}"
    prompt_path = _write_temp_talent_prompt(
        tmp_path,
        stem,
        '{\n  "schedule": "daily",\n  "priority": 10,\n  "tools": "journal"\n}',
    )
    try:
        configs = talent.get_talent_configs(include_disabled=True)
        assert stem in configs
        assert configs[stem].get("type") is None
    finally:
        prompt_path.unlink(missing_ok=True)


def test_get_talent_configs_raises_when_generate_missing_output(tmp_path, monkeypatch):
    talent = importlib.import_module("solstone.think.talent")
    monkeypatch.setattr(talent, "TALENT_DIR", tmp_path)
    stem = f"test_generate_missing_output_{uuid.uuid4().hex}"
    prompt_path = _write_temp_talent_prompt(
        tmp_path,
        stem,
        '{\n  "type": "generate",\n  "schedule": "daily",\n  "priority": 10\n}',
    )
    try:
        with pytest.raises(
            ValueError,
            match=rf"Prompt '{stem}'.*type='generate'.*missing required 'output'",
        ):
            talent.get_talent_configs(include_disabled=True)
    finally:
        prompt_path.unlink(missing_ok=True)


def test_get_talent_configs_allows_cogitate_without_tools(tmp_path, monkeypatch):
    talent = importlib.import_module("solstone.think.talent")
    monkeypatch.setattr(talent, "TALENT_DIR", tmp_path)
    stem = f"test_cogitate_missing_tools_{uuid.uuid4().hex}"
    prompt_path = _write_temp_talent_prompt(
        tmp_path,
        stem,
        '{\n  "type": "cogitate",\n  "schedule": "daily",\n  "priority": 10\n}',
    )
    try:
        configs = talent.get_talent_configs(include_disabled=True)
        assert stem in configs
        assert configs[stem]["type"] == "cogitate"
    finally:
        prompt_path.unlink(missing_ok=True)


def test_get_talent_configs_type_generate_returns_only_generate():
    talent = importlib.import_module("solstone.think.talent")
    generators = talent.get_talent_configs(type="generate")
    assert generators, "Expected at least one generate prompt"
    assert all(meta.get("type") == "generate" for meta in generators.values())


def test_get_talent_configs_type_cogitate_returns_only_cogitate():
    talent = importlib.import_module("solstone.think.talent")
    cogitate_prompts = talent.get_talent_configs(type="cogitate")
    assert cogitate_prompts, "Expected at least one cogitate prompt"
    assert all(meta.get("type") == "cogitate" for meta in cogitate_prompts.values())
