"""Tests for _detect_file_drop — file path detection that prevents
dragged/pasted absolute paths from being mistaken for slash commands."""

import os

import pytest

from cli import _detect_file_drop


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------

@pytest.fixture()
def tmp_image(tmp_path):
    """Create a temporary .png file and return its path."""
    img = tmp_path / "screenshot.png"
    img.write_bytes(b"\x89PNG\r\n\x1a\n")  # minimal PNG header
    return img


@pytest.fixture()
def tmp_text(tmp_path):
    """Create a temporary .py file and return its path."""
    f = tmp_path / "main.py"
    f.write_text("print('hello')\n")
    return f


@pytest.fixture()
def tmp_image_with_spaces(tmp_path):
    """Create a file whose name contains spaces (like macOS screenshots)."""
    img = tmp_path / "Screenshot 2026-04-01 at 7.25.32 PM.png"
    img.write_bytes(b"\x89PNG\r\n\x1a\n")
    return img


# ---------------------------------------------------------------------------
# Tests: returns None for non-file inputs
# ---------------------------------------------------------------------------

class TestNonFileInputs:
    def test_regular_slash_command(self):
        assert _detect_file_drop("/help") is None



    def test_empty_string(self):
        assert _detect_file_drop("") is None



    def test_nonexistent_path(self):
        assert _detect_file_drop("/nonexistent/path/to/file.png") is None


    def test_long_slash_command_does_not_raise(self):
        """Regression: long pasted slash commands like `/goal <long prose>`
        used to raise OSError(ENAMETOOLONG, errno 63 macOS / 36 Linux)
        from `Path.exists()` inside `_resolve_attachment_path`, which
        propagated up to `process_loop`'s catch-all and silently lost
        the user's input. The fix wraps the stat call in a try/except
        OSError and returns None, letting the slash-command dispatch
        path handle the input downstream.

        Reproducer: paste a `/goal` followed by ~430 chars of prose.
        Without the fix this triggers ENAMETOOLONG; with the fix it
        cleanly returns None (file-drop = no), so `_looks_like_slash_command`
        gets a chance to dispatch it.
        """
        # 430-char `/goal` payload — well above NAME_MAX (255 bytes) on
        # all common filesystems.
        long_goal = (
            "/goal " + ("Drive the board: triage triage-status items, "
                        "unblock spillover tasks where work is shipped, "
                        "advance P1 items by decomposing where needed. ") * 4
        )
        assert len(long_goal) > 255  # confirms it would have triggered ENAMETOOLONG
        assert _detect_file_drop(long_goal) is None



# ---------------------------------------------------------------------------
# Tests: image file detection
# ---------------------------------------------------------------------------

class TestImageFileDrop:
    def test_simple_image_path(self, tmp_image):
        result = _detect_file_drop(str(tmp_image))
        assert result is not None
        assert result["path"] == tmp_image
        assert result["is_image"] is True
        assert result["remainder"] == ""


    @pytest.mark.parametrize("ext", [".png", ".jpg", ".jpeg", ".gif", ".webp",
                                      ".bmp", ".tiff", ".tif", ".svg", ".ico"])
    def test_all_image_extensions(self, tmp_path, ext):
        img = tmp_path / f"test{ext}"
        img.write_bytes(b"fake")
        result = _detect_file_drop(str(img))
        assert result is not None
        assert result["is_image"] is True



# ---------------------------------------------------------------------------
# Tests: non-image file detection
# ---------------------------------------------------------------------------

class TestNonImageFileDrop:
    def test_python_file(self, tmp_text):
        result = _detect_file_drop(str(tmp_text))
        assert result is not None
        assert result["path"] == tmp_text
        assert result["is_image"] is False
        assert result["remainder"] == ""



# ---------------------------------------------------------------------------
# Tests: backslash-escaped spaces (macOS drag-and-drop)
# ---------------------------------------------------------------------------

class TestEscapedSpaces:
    def test_escaped_spaces_in_path(self, tmp_image_with_spaces):
        r"""macOS drags produce paths like /path/to/my\ file.png"""
        escaped = str(tmp_image_with_spaces).replace(' ', '\\ ')
        result = _detect_file_drop(escaped)
        assert result is not None
        assert result["path"] == tmp_image_with_spaces
        assert result["is_image"] is True


    def test_unquoted_spaces_in_path(self, tmp_image_with_spaces):
        result = _detect_file_drop(str(tmp_image_with_spaces))
        assert result is not None
        assert result["path"] == tmp_image_with_spaces
        assert result["is_image"] is True
        assert result["remainder"] == ""


    def test_mixed_escaped_and_literal_spaces_in_path(self, tmp_path):
        img = tmp_path / "Screenshot 2026-04-21 at 1.04.43 PM.png"
        img.write_bytes(b"\x89PNG\r\n\x1a\n")
        mixed = str(img).replace("Screenshot ", "Screenshot\\ ").replace("2026-04-21 ", "2026-04-21\\ ").replace("at ", "at\\ ")
        result = _detect_file_drop(mixed)
        assert result is not None
        assert result["path"] == img
        assert result["is_image"] is True
        assert result["remainder"] == ""


    def test_tilde_prefixed_path(self, tmp_path, monkeypatch):
        home = tmp_path / "home"
        img = home / "storage" / "shared" / "Pictures" / "cat.png"
        img.parent.mkdir(parents=True, exist_ok=True)
        img.write_bytes(b"\x89PNG\r\n\x1a\n")
        monkeypatch.setenv("HOME", str(home))
        # ntpath.expanduser ignores HOME (Python 3.8+) — it wants USERPROFILE.
        monkeypatch.setenv("USERPROFILE", str(home))

        result = _detect_file_drop("~/storage/shared/Pictures/cat.png what is this?")

        assert result is not None
        assert result["path"] == img
        assert result["is_image"] is True
        assert result["remainder"] == "what is this?"


    @pytest.mark.skipif(os.name != "nt", reason="Windows drive-letter URI contract")
    def test_windows_drive_letter_file_uri_drops_url_leading_slash(self, tmp_path):
        image = tmp_path / "drive-uri.png"
        image.write_bytes(b"\x89PNG\r\n\x1a\n")
        uri = image.as_uri()
        assert uri.startswith("file:///") and ":/" in uri

        result = _detect_file_drop(uri)

        assert result is not None
        assert result["path"] == image


# ---------------------------------------------------------------------------
# Tests: edge cases
# ---------------------------------------------------------------------------

class TestEdgeCases:
    def test_path_with_no_extension(self, tmp_path):
        f = tmp_path / "Makefile"
        f.write_text("all:\n\techo hi\n")
        result = _detect_file_drop(str(f))
        assert result is not None
        assert result["is_image"] is False

    def test_path_that_looks_like_command_but_is_file(self, tmp_path):
        """A file literally named 'help' inside a directory starting with /."""
        f = tmp_path / "help"
        f.write_text("not a command\n")
        result = _detect_file_drop(str(f))
        assert result is not None
        assert result["is_image"] is False

