"""Tests for the plugin lifecycle module."""

import os
from pathlib import Path
from unittest.mock import patch

import pytest

from statusline.plugin import (
    _BUILTINS,
    ensure_plugin,
    install,
    install_builtins,
    list_plugins,
    uninstall,
)


@pytest.fixture
def plugin_dir(tmp_path):
    """Create a temporary plugin directory and patch PLUGIN_DIR."""
    d = tmp_path / "statusline.d"
    d.mkdir()
    with patch("statusline.plugin.PLUGIN_DIR", d):
        yield d


class TestEnsurePlugin:
    def test_installs_metadata_if_missing(self, plugin_dir):
        path = ensure_plugin("metadata")
        assert path.exists()
        assert path.name == "00-metadata.py"
        assert path.stat().st_mode & 0o111  # executable
        assert path.parent == plugin_dir

    def test_installs_chatmap_if_missing(self, plugin_dir):
        path = ensure_plugin("chatmap")
        assert path.exists()
        assert path.name == "10-chatmap.py"

    def test_idempotent(self, plugin_dir):
        path1 = ensure_plugin("metadata")
        mtime1 = path1.stat().st_mtime
        path2 = ensure_plugin("metadata")
        # Should return existing file without recopying
        assert path1 == path2

    def test_finds_existing_with_different_prefix(self, plugin_dir):
        """If user renamed to 05-metadata.py, ensure_plugin should find it."""
        custom = plugin_dir / "05-metadata.py"
        custom.write_text("#!/usr/bin/env python3\n# custom\n")
        custom.chmod(0o755)

        path = ensure_plugin("metadata")
        assert path == custom  # Should return existing, not overwrite

    def test_raises_for_unknown_plugin(self, plugin_dir):
        with pytest.raises(ValueError, match="Unknown built-in"):
            ensure_plugin("nonexistent")


class TestListPlugins:
    def test_lists_installed_plugins(self, plugin_dir):
        ensure_plugin("metadata")
        ensure_plugin("chatmap")

        plugins = list_plugins()
        names = [name for _, name, _ in plugins]
        assert "metadata" in names
        assert "chatmap" in names

    def test_empty_when_no_plugins(self, plugin_dir):
        # Remove all files
        for f in plugin_dir.iterdir():
            f.unlink()
        assert list_plugins() == []

    def test_reports_disabled_plugins(self, plugin_dir):
        p = plugin_dir / "00-test.py"
        p.write_text("#!/usr/bin/env python3\n")
        p.chmod(0o644)  # not executable

        plugins = list_plugins()
        assert len(plugins) == 1
        _, name, enabled = plugins[0]
        assert name == "test"
        assert enabled is False


class TestInstallUninstall:
    def test_install_copies_file(self, plugin_dir, tmp_path):
        src = tmp_path / "myplugin.py"
        src.write_text("#!/usr/bin/env python3\nprint('hello')\n")

        dest = install(src, "50-myplugin.py")
        assert dest.exists()
        assert dest.name == "50-myplugin.py"
        assert dest.stat().st_mode & 0o111

    def test_install_rejects_bad_name(self, plugin_dir, tmp_path):
        src = tmp_path / "myplugin.py"
        src.write_text("#!/usr/bin/env python3\n")

        with pytest.raises(ValueError, match="NN-name.ext"):
            install(src, "bad-name.py")

    def test_install_missing_file(self, plugin_dir, tmp_path):
        with pytest.raises(FileNotFoundError):
            install(tmp_path / "nope.py")

    def test_uninstall_removes_plugin(self, plugin_dir):
        ensure_plugin("metadata")
        assert any(plugin_dir.iterdir())

        removed = uninstall("metadata")
        assert removed is not None
        assert not removed.exists()

    def test_uninstall_nonexistent_returns_none(self, plugin_dir):
        assert uninstall("nonexistent") is None


class TestInstallBuiltins:
    def test_installs_all_builtins(self, plugin_dir):
        installed = install_builtins()
        assert len(installed) == len(_BUILTINS)
        for p in installed:
            assert p.exists()
            assert p.stat().st_mode & 0o111

    def test_overwrites_existing(self, plugin_dir):
        """install_builtins should update even if files exist."""
        old = plugin_dir / "00-metadata.py"
        old.write_text("# old version")
        old.chmod(0o755)

        install_builtins()

        # Content should be updated
        assert old.read_text() != "# old version"
