import os
import subprocess
from pathlib import Path

import pytest

from pytest_cream.install import install_from_git


def test_install_pip_success(monkeypatch):
    calls = []

    def fake_run(cmd, check=True, **kwargs):
        calls.append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git("owner/repo", branch="feature-x", mode="pip")
    assert res["status"] == "ok"
    assert res["path"] is None
    assert "installed via pip" in res["message"]
    # ensure pip install git+...@branch was called
    assert any("pip" in c[2] for c in calls if isinstance(c, list))


def test_install_editable_success(monkeypatch, tmp_path):
    # workspace provided so we can assert path
    workspace = str(tmp_path)
    expected_dest = os.path.join(workspace, "repo_clone")

    sequence = {"called": []}

    def fake_run(cmd, check=True, **kwargs):
        sequence["called"].append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git(
        "owner/repo", branch="dev", mode="editable", workspace=workspace
    )
    assert res["status"] == "ok"
    assert res["path"] == expected_dest
    assert "installed editable" in res["message"]
    # verify clone and pip -e were attempted
    assert any(cmd[0] == "git" and "clone" in cmd for cmd in sequence["called"])
    assert any("-e" in cmd for cmd in sequence["called"])


def test_install_wheel_no_wheel(monkeypatch, tmp_path):
    workspace = str(tmp_path)
    dest = os.path.join(workspace, "repo_clone")
    # create dest and dist dirs structure but leave dist empty
    os.makedirs(dest, exist_ok=True)
    dist = os.path.join(dest, "dist")
    os.makedirs(dist, exist_ok=True)

    def fake_run(cmd, check=True, **kwargs):
        # succeed for clone/install/build commands
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    # make isdir and listdir work normally (dist exists but empty)
    res = install_from_git("owner/repo", branch=None, mode="wheel", workspace=workspace)
    assert res["status"] == "error"
    assert res["message"] == "no wheel produced"
    assert res["path"] == dest


def test_install_pip_failure(monkeypatch):
    def fake_run(cmd, check=True, **kwargs):
        # Simulate pip install failing
        raise subprocess.CalledProcessError(returncode=2, cmd=cmd, output=b"failed")

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git("owner/repo", branch="b", mode="pip")
    assert res["status"] == "error"
    # message is the stringified CalledProcessError on this platform
    assert "returned non-zero exit status" in res["message"]


def test_clone_failure(monkeypatch, tmp_path):
    workspace = str(tmp_path)

    def fake_run(cmd, check=True, **kwargs):
        # Fail on git clone
        if cmd and cmd[0] == "git":
            raise subprocess.CalledProcessError(
                returncode=128, cmd=cmd, output=b"git fail"
            )
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git(
        "owner/repo", branch="main", mode="editable", workspace=workspace
    )
    assert res["status"] == "error"
    assert "returned non-zero exit status" in res["message"]


def test_install_with_uv_pip_mode(monkeypatch):
    calls = []

    def fake_run(cmd, check=True, **kwargs):
        calls.append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git("owner/repo", branch="feature-x", mode="pip", use_uv=True)
    assert res["status"] == "ok"
    assert res["path"] is None
    assert "installed via pip" in res["message"]
    # ensure uv pip install was called instead of python -m pip
    assert any("uv" in c and "pip" in c for c in calls if isinstance(c, list))


def test_install_with_uv_editable_mode(monkeypatch, tmp_path):
    workspace = str(tmp_path)
    expected_dest = os.path.join(workspace, "repo_clone")

    sequence = {"called": []}

    def fake_run(cmd, check=True, **kwargs):
        sequence["called"].append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git(
        "owner/repo", branch="dev", mode="editable", workspace=workspace, use_uv=True
    )
    assert res["status"] == "ok"
    assert res["path"] == expected_dest
    assert "installed editable" in res["message"]
    # verify clone and uv pip -e were attempted
    assert any(cmd[0] == "git" and "clone" in cmd for cmd in sequence["called"])
    assert any("uv" in cmd and "-e" in cmd for cmd in sequence["called"])


def test_install_with_uv_not_available(monkeypatch):
    def fake_run(cmd, check=True, **kwargs):
        if cmd[0] == "uv":
            raise subprocess.CalledProcessError(returncode=1, cmd=cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git("owner/repo", mode="pip", use_uv=True)
    assert res["status"] == "error"
    assert "uv not found" in res["message"]


def test_install_with_custom_command(monkeypatch, tmp_path):
    workspace = str(tmp_path)
    expected_dest = os.path.join(workspace, "repo_clone")

    sequence = {"called": []}

    def fake_run(cmd, check=True, **kwargs):
        sequence["called"].append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    res = install_from_git(
        "owner/repo", 
        branch="dev", 
        workspace=workspace,
        install_cmd="uv sync --extra cpu"
    )
    assert res["status"] == "ok"
    assert res["path"] == expected_dest
    assert "installed with custom command" in res["message"]
    assert "uv sync --extra cpu" in res["message"]
    # verify clone was attempted
    assert any(cmd[0] == "git" and "clone" in cmd for cmd in sequence["called"])
    # verify custom command was run
    assert any(cmd == ["uv", "sync", "--extra", "cpu"] for cmd in sequence["called"])


def test_install_with_custom_command_overrides_mode(monkeypatch, tmp_path):
    workspace = str(tmp_path)

    sequence = {"called": []}

    def fake_run(cmd, check=True, **kwargs):
        sequence["called"].append(cmd)
        return subprocess.CompletedProcess(cmd, 0)

    monkeypatch.setattr(subprocess, "run", fake_run)

    # Even though mode=pip, custom command should take precedence
    res = install_from_git(
        "owner/repo",
        mode="pip",
        workspace=workspace,
        install_cmd="poetry install --extras dev"
    )
    assert res["status"] == "ok"
    assert "custom command" in res["message"]
    # Should not see pip commands, only git clone and poetry
    pip_commands = [cmd for cmd in sequence["called"] if "pip" in str(cmd)]
    assert len(pip_commands) == 0
    # Should see poetry command
    assert any(cmd == ["poetry", "install", "--extras", "dev"] for cmd in sequence["called"])
