"""Tests for the Excel (.xlsx) 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, "excel"))


# --- create ---


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


async def test_create_with_data(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    data = [["Name", "Age"], ["Alice", 30], ["Bob", 25]]
    result = await skill.execute({"action": "create", "path": path, "data": data})
    assert result["success"] is True
    assert result["data"]["rows"] == 3


async def test_create_with_sheet_name(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    result = await skill.execute({"action": "create", "path": path, "sheet": "Sales"})
    assert result["data"]["sheet"] == "Sales"


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


# --- read ---


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

    result = await skill.execute({"action": "read", "path": path})
    assert result["success"] is True
    assert result["data"]["rows"] == 2
    assert result["data"]["values"][0] == ["Name", "Age"]
    assert result["data"]["values"][1] == ["Alice", 30]


async def test_read_specific_sheet(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    await skill.execute({"action": "create", "path": path, "sheet": "Data"})
    await skill.execute({"action": "write", "path": path, "sheet": "Data", "data": [["x", 1]]})

    result = await skill.execute({"action": "read", "path": path, "sheet": "Data"})
    assert result["data"]["sheet"] == "Data"
    assert result["data"]["values"][0] == ["x", 1]


async def test_read_with_range(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    data = [["A", "B", "C"], [1, 2, 3], [4, 5, 6]]
    await skill.execute({"action": "create", "path": path, "data": data})

    result = await skill.execute({"action": "read", "path": path, "range": "A1:B2"})
    assert result["data"]["rows"] == 2
    assert result["data"]["values"][0] == ["A", "B"]


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


# --- write ---


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

    result = await skill.execute({"action": "write", "path": path, "data": [["x", 1], ["y", 2]]})
    assert result["success"] is True
    assert result["data"]["cells_written"] == 4

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


async def test_write_creates_new_file(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "new.xlsx")
    result = await skill.execute({"action": "write", "path": path, "data": [["hello"]]})
    assert result["success"] is True
    assert os.path.isfile(path)


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

    await skill.execute({"action": "write", "path": path, "data": [["val"]], "start_cell": "C3"})

    read_result = await skill.execute({"action": "read", "path": path})
    values = read_result["data"]["values"]
    # Row 3 (index 2), Col C (index 2)
    assert values[2][2] == "val"


async def test_write_missing_data(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    with pytest.raises(ValueError, match="'data' is required"):
        await skill.execute({"action": "write", "path": path})


# --- list_sheets ---


async def test_list_sheets(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    await skill.execute({"action": "create", "path": path, "sheet": "First"})
    await skill.execute({"action": "add_sheet", "path": path, "title": "Second"})

    result = await skill.execute({"action": "list_sheets", "path": path})
    assert result["success"] is True
    assert "First" in result["data"]["sheets"]
    assert "Second" in result["data"]["sheets"]


# --- add_sheet ---


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

    result = await skill.execute({"action": "add_sheet", "path": path, "title": "NewSheet"})
    assert result["success"] is True
    assert result["data"]["sheet"] == "NewSheet"


async def test_add_sheet_duplicate(tmp_path):
    skill = _load_skill()
    path = str(tmp_path / "test.xlsx")
    await skill.execute({"action": "create", "path": path, "sheet": "Existing"})

    with pytest.raises(ValueError, match="already exists"):
        await skill.execute({"action": "add_sheet", "path": path, "title": "Existing"})


# --- delete ---


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


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