"""Tests for the editor integration module (editor.py).

Tests cover:
- get_editor: environment variable priority (SKILARK_EDITOR > VISUAL > EDITOR)
- get_editor: fallback to nano then vi when nothing is set
- open_in_editor: file extension matches language (.py for python, .java for java)
- open_in_editor: returns content after editor exits
"""

import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from skilark_cli.editor import get_editor, open_in_editor


# ---------------------------------------------------------------------------
# get_editor
# ---------------------------------------------------------------------------


class TestGetEditor:
    def test_prefers_skilark_editor(self, monkeypatch):
        monkeypatch.setenv("SKILARK_EDITOR", "vim")
        monkeypatch.delenv("VISUAL", raising=False)
        monkeypatch.delenv("EDITOR", raising=False)
        assert get_editor() == "vim"

    def test_skilark_editor_takes_priority_over_visual(self, monkeypatch):
        monkeypatch.setenv("SKILARK_EDITOR", "emacs")
        monkeypatch.setenv("VISUAL", "code")
        assert get_editor() == "emacs"

    def test_falls_back_to_visual(self, monkeypatch):
        monkeypatch.delenv("SKILARK_EDITOR", raising=False)
        monkeypatch.setenv("VISUAL", "code")
        monkeypatch.delenv("EDITOR", raising=False)
        assert get_editor() == "code"

    def test_falls_back_to_editor(self, monkeypatch):
        monkeypatch.delenv("SKILARK_EDITOR", raising=False)
        monkeypatch.delenv("VISUAL", raising=False)
        monkeypatch.setenv("EDITOR", "nano")
        assert get_editor() == "nano"

    def test_falls_back_to_nano_when_available(self, monkeypatch):
        monkeypatch.delenv("SKILARK_EDITOR", raising=False)
        monkeypatch.delenv("VISUAL", raising=False)
        monkeypatch.delenv("EDITOR", raising=False)
        with patch("shutil.which", side_effect=lambda cmd: "/usr/bin/nano" if cmd == "nano" else None):
            result = get_editor()
        assert result == "nano"

    def test_falls_back_to_vi_when_nano_unavailable(self, monkeypatch):
        monkeypatch.delenv("SKILARK_EDITOR", raising=False)
        monkeypatch.delenv("VISUAL", raising=False)
        monkeypatch.delenv("EDITOR", raising=False)
        with patch("shutil.which", return_value=None):
            result = get_editor()
        assert result == "vi"


# ---------------------------------------------------------------------------
# open_in_editor
# ---------------------------------------------------------------------------


class TestOpenInEditor:
    def test_creates_py_file_for_python(self, tmp_path, monkeypatch):
        """The temp file written for Python problems must have a .py extension."""
        created_paths = []

        def fake_subprocess_run(cmd, **kwargs):
            # Record the file path from the command
            created_paths.append(cmd[-1])
            # Write some content to simulate editing
            Path(cmd[-1]).write_text("def solution():\n    return 42\n")
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("SKILARK_EDITOR", "fake-editor")
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            open_in_editor("def solution():\n    pass\n", "python")

        assert created_paths, "subprocess.run was never called"
        assert created_paths[0].endswith(".py"), f"Expected .py, got: {created_paths[0]}"

    def test_creates_java_file_for_java(self, tmp_path, monkeypatch):
        """The temp file for Java problems must have a .java extension."""
        created_paths = []

        def fake_subprocess_run(cmd, **kwargs):
            created_paths.append(cmd[-1])
            Path(cmd[-1]).write_text("class Solution {\n}\n")
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("SKILARK_EDITOR", "fake-editor")
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            open_in_editor("class Solution {\n}\n", "java")

        assert created_paths[0].endswith(".java"), f"Expected .java, got: {created_paths[0]}"

    def test_returns_edited_content(self, monkeypatch):
        """open_in_editor must return whatever the editor wrote to the file."""
        edited_content = "def solution():\n    return 99  # edited\n"

        def fake_subprocess_run(cmd, **kwargs):
            Path(cmd[-1]).write_text(edited_content)
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("SKILARK_EDITOR", "fake-editor")
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            result = open_in_editor("def solution():\n    pass\n", "python")

        assert result == edited_content

    def test_starter_code_written_to_file(self, monkeypatch):
        """The starter code must be pre-populated in the file before the editor opens."""
        starter = "def two_sum(nums, target):\n    # your code here\n    pass\n"
        written_content = []

        def fake_subprocess_run(cmd, **kwargs):
            # Read the file before "editing" it to verify starter code is there
            written_content.append(Path(cmd[-1]).read_text())
            # Simulate editor keeping the content unchanged
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("SKILARK_EDITOR", "fake-editor")
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            open_in_editor(starter, "python")

        assert written_content, "subprocess.run was never called"
        assert starter in written_content[0]

    def test_editor_with_flags_is_split_correctly(self, monkeypatch):
        """An editor value like 'code --wait' must be split into ['code', '--wait']."""
        received_cmd = []

        def fake_subprocess_run(cmd, **kwargs):
            received_cmd.extend(cmd)
            Path(cmd[-1]).write_text("edited\n")
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("VISUAL", "code --wait")
        monkeypatch.delenv("SKILARK_EDITOR", raising=False)
        monkeypatch.delenv("EDITOR", raising=False)
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            open_in_editor("def solution():\n    pass\n", "python")

        assert received_cmd[0] == "code"
        assert received_cmd[1] == "--wait"
        assert received_cmd[2].endswith(".py")

    def test_temp_file_cleaned_up_after_editing(self, monkeypatch):
        """The temporary file must be deleted after open_in_editor returns."""
        temp_file_path = []

        def fake_subprocess_run(cmd, **kwargs):
            temp_file_path.append(cmd[-1])
            Path(cmd[-1]).write_text("def solution():\n    return 1\n")
            result = MagicMock()
            result.returncode = 0
            return result

        monkeypatch.setenv("SKILARK_EDITOR", "fake-editor")
        with patch("subprocess.run", side_effect=fake_subprocess_run):
            open_in_editor("def solution():\n    pass\n", "python")

        assert temp_file_path, "subprocess.run was never called"
        assert not Path(temp_file_path[0]).exists(), "Temp file was not cleaned up"
