"""Tests for the PowerPoint (.pptx) skill."""

import os

import pytest

from fliiq.runtime.package_data import bundled_skills_dir
from fliiq.runtime.skills.base import SkillBase

SKILLS_DIR = str(bundled_skills_dir())


def _load_skill() -> SkillBase:
    return SkillBase(os.path.join(SKILLS_DIR, "powerpoint"))


# --- create ---


async def test_create_empty(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    result = await skill.execute({"action": "create", "path": path})
    assert result["success"] is True
    assert os.path.isfile(path)
    assert result["data"]["slides"] == 0


async def test_create_with_title(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    result = await skill.execute({"action": "create", "path": path, "title": "My Deck", "subtitle": "Q1 2026"})
    assert result["success"] is True
    assert result["data"]["slides"] == 1


async def test_create_nested_dirs(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "a" / "b" / "test.pptx")
    result = await skill.execute({"action": "create", "path": path})
    assert result["success"] is True
    assert os.path.isfile(path)


# --- read ---


async def test_read_with_content(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Slide Title"})

    result = await skill.execute({"action": "read", "path": path})
    assert result["success"] is True
    assert len(result["data"]["slides"]) == 1
    assert "Slide Title" in result["data"]["slides"][0]["text"]


async def test_read_empty_deck(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path})

    result = await skill.execute({"action": "read", "path": path})
    assert result["success"] is True
    assert len(result["data"]["slides"]) == 0


async def test_read_not_found():
    skill = _load_skill()
    with pytest.raises(FileNotFoundError):
        await skill.execute({"action": "read", "path": "/nonexistent/file.pptx"})


# --- add_slide ---


async def test_add_slide_with_content(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Title Slide"})

    result = await skill.execute({
        "action": "add_slide",
        "path": path,
        "title": "Agenda",
        "body": "Item 1\nItem 2\nItem 3",
    })
    assert result["success"] is True
    assert result["data"]["total_slides"] == 2

    read_result = await skill.execute({"action": "read", "path": path})
    slide_texts = read_result["data"]["slides"][1]["text"]
    assert "Agenda" in slide_texts


async def test_add_slide_blank(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path})

    result = await skill.execute({"action": "add_slide", "path": path, "layout": "BLANK"})
    assert result["success"] is True


async def test_add_slide_invalid_layout(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path})

    with pytest.raises(ValueError, match="Unknown layout"):
        await skill.execute({"action": "add_slide", "path": path, "layout": "INVALID"})


async def test_add_slide_file_not_found():
    skill = _load_skill()
    with pytest.raises(FileNotFoundError):
        await skill.execute({"action": "add_slide", "path": "/nonexistent/file.pptx"})


# --- modify_slide ---


async def test_modify_slide(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Old Title"})

    result = await skill.execute({
        "action": "modify_slide",
        "path": path,
        "slide_index": 0,
        "old_text": "Old Title",
        "new_text": "New Title",
    })
    assert result["success"] is True
    assert result["data"]["replacements"] >= 1

    read_result = await skill.execute({"action": "read", "path": path})
    assert "New Title" in read_result["data"]["slides"][0]["text"]


async def test_modify_slide_text_not_found(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Hello"})

    with pytest.raises(ValueError, match="not found"):
        await skill.execute({
            "action": "modify_slide",
            "path": path,
            "slide_index": 0,
            "old_text": "Nonexistent",
            "new_text": "Replacement",
        })


async def test_modify_slide_out_of_range(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Hello"})

    with pytest.raises(ValueError, match="out of range"):
        await skill.execute({
            "action": "modify_slide",
            "path": path,
            "slide_index": 5,
            "old_text": "Hello",
            "new_text": "World",
        })


async def test_modify_slide_missing_params(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Hello"})

    with pytest.raises(ValueError):
        await skill.execute({"action": "modify_slide", "path": path, "slide_index": 0})


# --- delete_slide ---


async def test_delete_slide(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Slide 0"})
    await skill.execute({"action": "add_slide", "path": path, "title": "Slide 1"})

    result = await skill.execute({"action": "delete_slide", "path": path, "slide_index": 0})
    assert result["success"] is True

    read_result = await skill.execute({"action": "read", "path": path})
    assert len(read_result["data"]["slides"]) == 1


async def test_delete_slide_out_of_range(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path, "title": "Hello"})

    with pytest.raises(ValueError, match="out of range"):
        await skill.execute({"action": "delete_slide", "path": path, "slide_index": 5})


# --- delete ---


async def test_delete(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.pptx")
    await skill.execute({"action": "create", "path": path})
    assert os.path.isfile(path)

    result = await skill.execute({"action": "delete", "path": path})
    assert result["success"] is True
    assert not os.path.isfile(path)


async def test_delete_not_found():
    skill = _load_skill()
    with pytest.raises(FileNotFoundError):
        await skill.execute({"action": "delete", "path": "/nonexistent/file.pptx"})


# --- security ---


async def test_security_check():
    skill = _load_skill()
    with pytest.raises(PermissionError):
        await skill.execute({"action": "read", "path": os.path.expanduser("~/.ssh/id_rsa")})


# --- missing path ---


async def test_missing_path():
    skill = _load_skill()
    with pytest.raises(ValueError, match="'path' is required"):
        await skill.execute({"action": "read"})


# --- unknown action ---


async def test_unknown_action():
    skill = _load_skill()
    with pytest.raises(ValueError, match="Unknown action"):
        await skill.execute({"action": "bogus", "path": "/tmp/x.pptx"})
