import json
import os
import stat
from types import SimpleNamespace
from uuid import uuid4

import pytest

import instavm.cli as cli
from instavm import _cli_config as cli_config


def _set_home(monkeypatch, tmp_path):
    monkeypatch.setattr(cli_config, "get_home_dir", lambda: tmp_path)


def test_update_profile_settings_writes_secure_config(tmp_path, monkeypatch):
    _set_home(monkeypatch, tmp_path)

    config_path = cli_config.update_profile_settings(
        api_key="instavm_sk_test_1234567890",
        base_url="https://api.instavm.io",
    )

    assert config_path == tmp_path / ".instavm" / "config.json"
    payload = json.loads(config_path.read_text(encoding="utf-8"))
    assert payload["schema_version"] == 1
    assert payload["profiles"]["default"]["auth"]["api_key"] == "instavm_sk_test_1234567890"
    assert payload["profiles"]["default"]["base_url"] == "https://api.instavm.io"

    if os.name != "nt":
        assert stat.S_IMODE(config_path.parent.stat().st_mode) == 0o700
        assert stat.S_IMODE(config_path.stat().st_mode) == 0o600


def test_resolve_runtime_config_prefers_flags_then_env_then_config(tmp_path, monkeypatch):
    _set_home(monkeypatch, tmp_path)
    cli_config.update_profile_settings(api_key="stored_key")

    monkeypatch.setenv("INSTAVM_API_KEY", "env_key")
    monkeypatch.setenv("INSTAVM_BASE_URL", "https://staging.api.instavm.io")

    resolved = cli_config.resolve_runtime_config(api_key="flag_key")
    assert resolved.api_key == "flag_key"
    assert resolved.api_key_source == "flag"
    assert resolved.base_url == "https://staging.api.instavm.io"
    assert resolved.base_url_source == "env"
    assert resolved.ssh_host == "staging.instavm.dev"
    assert resolved.ssh_host_source == "derived"

    fallback = cli_config.resolve_runtime_config()
    assert fallback.api_key == "env_key"
    assert fallback.api_key_source == "env"


def test_redact_secret_and_ssh_host_derivation():
    assert cli_config.redact_secret("instavm_sk_1234567890") == "instavm_...7890"
    assert cli_config.derive_ssh_host("https://api.instavm.io") == "instavm.dev"
    assert cli_config.derive_ssh_host("https://staging.api.instavm.io") == "staging.instavm.dev"


def test_auth_set_key_and_status_json(tmp_path, monkeypatch, capsys):
    _set_home(monkeypatch, tmp_path)
    monkeypatch.setattr(cli.sys.stdin, "isatty", lambda: True)
    monkeypatch.setattr(cli.getpass, "getpass", lambda prompt: "instavm_sk_test_1234567890")

    assert cli.main(["auth", "set-key", "--json"]) == 0
    set_key_payload = json.loads(capsys.readouterr().out)
    assert set_key_payload["status"] == "stored"

    assert cli.main(["auth", "status", "--json"]) == 0
    status_payload = json.loads(capsys.readouterr().out)
    assert status_payload["api_key"]["source"] == "config"
    assert status_payload["api_key"]["configured"] is True
    assert status_payload["api_key"]["redacted"] == "instavm_...7890"


def test_auth_set_key_reads_stdin_when_not_tty(tmp_path, monkeypatch, capsys):
    _set_home(monkeypatch, tmp_path)
    monkeypatch.setattr(cli.sys.stdin, "isatty", lambda: False)
    monkeypatch.setattr(cli.sys.stdin, "read", lambda: "instavm_sk_pipe_1234567890\n")

    assert cli.main(["auth", "set-key", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["status"] == "stored"
    assert payload["api_key"] == "instavm_...7890"


def test_whoami_json_output(monkeypatch, capsys):
    class FakeClient:
        def __init__(self, **kwargs):
            self.kwargs = kwargs

        def get_current_user(self):
            return {"id": 7, "email": "cli@example.com", "name": "CLI", "is_verified": True}

        def list_ssh_keys(self):
            return [{"id": 11, "fingerprint": "SHA256:abc", "comment": "laptop"}]

    monkeypatch.setattr(cli, "InstaVM", FakeClient)

    assert cli.main(["whoami", "--api-key", "instavm_sk_test", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["email"] == "cli@example.com"
    assert payload["ssh_keys"][0]["fingerprint"] == "SHA256:abc"


def test_share_create_json_output(monkeypatch, capsys):
    class FakeShares:
        def create(self, **kwargs):
            return {
                "share_id": "share_123",
                "url": "https://share_123.instavm.site/",
                "port": kwargs["port"],
                "is_public": kwargs["is_public"],
            }

    class FakeClient:
        def __init__(self, **kwargs):
            self.shares = FakeShares()

    monkeypatch.setattr(cli, "InstaVM", FakeClient)

    assert cli.main(["share", "create", "vm_123", "3000", "--public", "--api-key", "instavm_sk_test", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["share_id"] == "share_123"
    assert payload["is_public"] is True


def test_volume_ls_json_output(monkeypatch, capsys):
    class FakeVolumes:
        def list(self, refresh_usage=False):
            assert refresh_usage is True
            return [
                {
                    "id": "vol_123",
                    "name": "workspace",
                    "quota_bytes": 5 * 1024**3,
                    "used_bytes": 1024,
                    "status": "available",
                }
            ]

    class FakeClient:
        def __init__(self, **kwargs):
            self.volumes = FakeVolumes()

    monkeypatch.setattr(cli, "InstaVM", FakeClient)

    assert cli.main(["volume", "ls", "--refresh", "--api-key", "instavm_sk_test", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["volumes"][0]["id"] == "vol_123"


def test_ssh_key_list_json_output(monkeypatch, capsys):
    class FakeClient:
        def __init__(self, **kwargs):
            pass

        def list_ssh_keys(self):
            return [{"id": 3, "fingerprint": "SHA256:def", "comment": "desktop"}]

    monkeypatch.setattr(cli, "InstaVM", FakeClient)

    assert cli.main(["ssh-key", "list", "--api-key", "instavm_sk_test", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["keys"][0]["fingerprint"] == "SHA256:def"


def test_desktop_status_json_output(monkeypatch, capsys):
    session_id = str(uuid4())

    class FakeComputerUse:
        def viewer_url(self, target_session_id):
            assert target_session_id == session_id
            return {"viewer_url": "https://viewer.example.com", "websocket_url": "wss://viewer.example.com/ws"}

    class FakeClient:
        def __init__(self, **kwargs):
            self.computer_use = FakeComputerUse()

        def get_session_status(self, target_session_id):
            assert target_session_id == session_id
            return {"session_id": target_session_id, "vm_id": "vm_desktop_123", "vm_status": "active", "vm_alive": True}

    monkeypatch.setattr(cli, "InstaVM", FakeClient)

    assert cli.main(["desktop", "status", session_id, "--api-key", "instavm_sk_test", "--json"]) == 0
    payload = json.loads(capsys.readouterr().out)
    assert payload["viewer_url"] == "https://viewer.example.com"
    assert payload["vm_id"] == "vm_desktop_123"


def test_connect_uses_derived_ssh_host(monkeypatch):
    commands = []

    monkeypatch.setattr(cli.shutil, "which", lambda name: f"/usr/bin/{name}")
    monkeypatch.setattr(
        cli.subprocess,
        "run",
        lambda command, check=False: commands.append(command) or SimpleNamespace(returncode=0),
    )

    assert cli.main([
        "connect",
        "--base-url",
        "https://staging.api.instavm.io",
        "vm_123",
        "--",
        "-L",
        "9000:localhost:80",
    ]) == 0
    assert commands == [["/usr/bin/ssh.exe", "vm_123@staging.instavm.dev", "-L", "9000:localhost:80"]]
