"""Tests for the Word (.docx) 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, "word"))


# --- create ---


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


async def test_create_with_text(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    result = await skill.execute({"action": "create", "path": path, "text": "Hello world"})
    assert result["success"] is True

    read_result = await skill.execute({"action": "read", "path": path})
    texts = [e["text"] for e in read_result["data"]["elements"] if e["type"] == "paragraph"]
    assert "Hello world" in texts


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


# --- read ---


async def test_read_paragraphs(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path, "text": "First paragraph"})
    await skill.execute({"action": "add_content", "path": path, "text": "Second paragraph"})

    result = await skill.execute({"action": "read", "path": path})
    assert result["success"] is True
    paragraphs = [e for e in result["data"]["elements"] if e["type"] == "paragraph"]
    assert len(paragraphs) >= 2


async def test_read_headings(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path})
    await skill.execute({"action": "add_content", "path": path, "content_type": "heading", "text": "Title", "level": 1})
    await skill.execute({"action": "add_content", "path": path, "content_type": "heading", "text": "Subtitle", "level": 2})

    result = await skill.execute({"action": "read", "path": path})
    headings = [e for e in result["data"]["elements"] if e["type"] == "heading"]
    assert len(headings) == 2
    assert headings[0]["level"] == 1
    assert headings[1]["level"] == 2


async def test_read_tables(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path})
    await skill.execute({
        "action": "add_content",
        "path": path,
        "content_type": "table",
        "data": [["Name", "Age"], ["Alice", "30"]],
    })

    result = await skill.execute({"action": "read", "path": path})
    tables = [e for e in result["data"]["elements"] if e["type"] == "table"]
    assert len(tables) == 1
    assert tables[0]["rows"] == 2
    assert tables[0]["data"][0] == ["Name", "Age"]


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


# --- add_content ---


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

    result = await skill.execute({
        "action": "add_content",
        "path": path,
        "content_type": "heading",
        "text": "Chapter 1",
        "level": 1,
    })
    assert result["success"] is True
    assert "heading" in result["message"]


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

    result = await skill.execute({
        "action": "add_content",
        "path": path,
        "content_type": "paragraph",
        "text": "Some long paragraph text here.",
    })
    assert result["success"] is True


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

    result = await skill.execute({
        "action": "add_content",
        "path": path,
        "content_type": "table",
        "data": [["Col A", "Col B"], ["1", "2"]],
    })
    assert result["success"] is True
    assert "2x2" in result["message"]


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

    with pytest.raises(ValueError, match="Heading level must be 1-4"):
        await skill.execute({
            "action": "add_content",
            "path": path,
            "content_type": "heading",
            "text": "Bad",
            "level": 10,
        })


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

    with pytest.raises(ValueError, match="'text' is required"):
        await skill.execute({"action": "add_content", "path": path, "content_type": "paragraph"})


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

    with pytest.raises(ValueError, match="'data'"):
        await skill.execute({"action": "add_content", "path": path, "content_type": "table"})


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

    with pytest.raises(ValueError, match="Unknown content_type"):
        await skill.execute({"action": "add_content", "path": path, "content_type": "image"})


async def test_add_content_file_not_found():
    skill = _load_skill()
    with pytest.raises(FileNotFoundError):
        await skill.execute({"action": "add_content", "path": "/nonexistent/file.docx", "text": "hi"})


# --- replace_text ---


async def test_replace_text(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path, "text": "Hello world"})

    result = await skill.execute({
        "action": "replace_text",
        "path": path,
        "old_text": "Hello",
        "new_text": "Goodbye",
    })
    assert result["success"] is True
    assert result["data"]["replacements"] >= 1

    read_result = await skill.execute({"action": "read", "path": path})
    texts = [e["text"] for e in read_result["data"]["elements"] if e["type"] == "paragraph"]
    assert any("Goodbye" in t for t in texts)


async def test_replace_text_not_found(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path, "text": "Hello"})

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


async def test_replace_text_missing_params(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    await skill.execute({"action": "create", "path": path, "text": "Hello"})

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


# --- delete ---


async def test_delete(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.docx")
    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.docx"})


# --- 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.docx"})
