import sys
from types import SimpleNamespace

import pytest


class _Socket:
    def __init__(self):
        self.closed = False

    def close(self):
        self.closed = True


class _Key:
    def asbytes(self):
        return b"pinned-server-key"


class _Transport:
    last = None

    def __init__(self, sock):
        self.sock = sock
        self.banner_timeout = None
        self.auth_timeout = None
        self.remote_version = "SSH-2.0-test-server"
        self.authenticated = False
        self.closed = False
        self.auth_call = None
        type(self).last = self

    def start_client(self, timeout):
        self.start_timeout = timeout

    def get_remote_server_key(self):
        return _Key()

    def auth_password(self, username, password, fallback):
        self.auth_call = (username, password, fallback)
        self.authenticated = True

    def is_authenticated(self):
        return self.authenticated

    def close(self):
        self.closed = True


def _pinned_fingerprint():
    from app.core.ssh_identity import _fingerprint_for_key

    return "SHA256:" + _fingerprint_for_key(_Key())


def test_probe_blocks_without_a_pinned_host_key(monkeypatch):
    from app.core import ssh_identity

    monkeypatch.setattr(ssh_identity.socket, "create_connection", lambda *_: pytest.fail("network must not open"))
    monkeypatch.setattr(ssh_identity, "decrypt_secret", lambda _: pytest.fail("secret must not decrypt"))

    result = ssh_identity.test_pinned_ssh_identity(
        host="192.0.2.10", port=22, username="admin", encrypted_secret="ciphertext", expected_host_key_fingerprint=None
    )

    assert result.ok is False
    assert "diblokir" in result.message


def test_probe_verifies_pin_before_decrypting_and_sends_no_commands(monkeypatch):
    from app.core import ssh_identity

    sock = _Socket()
    monkeypatch.setattr(ssh_identity.socket, "create_connection", lambda target, timeout: sock)
    monkeypatch.setitem(sys.modules, "paramiko", SimpleNamespace(Transport=_Transport))
    monkeypatch.setattr(ssh_identity, "decrypt_secret", lambda encrypted: "test-only-password")

    result = ssh_identity.test_pinned_ssh_identity(
        host="192.0.2.10", port=22, username="admin", encrypted_secret="ciphertext", expected_host_key_fingerprint=_pinned_fingerprint()
    )

    assert result.ok is True
    assert result.server_fingerprint == _pinned_fingerprint()
    assert result.server_banner == "SSH-2.0-test-server"
    assert _Transport.last.auth_call == ("admin", "test-only-password", False)
    assert _Transport.last.closed is True


def test_probe_rejects_an_unpinned_server_before_secret_decryption(monkeypatch):
    from app.core import ssh_identity

    monkeypatch.setattr(ssh_identity.socket, "create_connection", lambda target, timeout: _Socket())
    monkeypatch.setitem(sys.modules, "paramiko", SimpleNamespace(Transport=_Transport))
    monkeypatch.setattr(ssh_identity, "decrypt_secret", lambda _: pytest.fail("secret must not decrypt"))

    result = ssh_identity.test_pinned_ssh_identity(
        host="192.0.2.10", port=22, username="admin", encrypted_secret="ciphertext", expected_host_key_fingerprint="SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
    )

    assert result.ok is False
    assert "tidak cocok" in result.message
    assert _Transport.last.closed is True


@pytest.mark.asyncio
async def test_assigned_olt_endpoint_requires_the_pinned_identity_probe(monkeypatch):
    from app.routes import devices
    from app.core.ssh_identity import SSHIdentityTestResult

    device = SimpleNamespace(
        id=1, name="OLT-UJI", vendor="C-DATA", model="FD1601S-B1", host="192.0.2.10",
        management_protocol="ssh", api_port=22, credential_id=2, is_active=True,
    )
    credential = SimpleNamespace(id=2, protocol="ssh", is_active=True, username="admin", encrypted_secret="ciphertext")

    class FakeDB:
        async def get(self, model, item_id):
            return device if item_id == 1 else credential

    seen = {}

    def fake_probe(**kwargs):
        seen.update(kwargs)
        return SSHIdentityTestResult(True, "verified", "SHA256:test", "SSH-2.0-test")

    monkeypatch.setattr(devices, "test_pinned_ssh_identity", fake_probe)
    monkeypatch.setenv("OLT_1_SSH_HOST_KEY_SHA256", "SHA256:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

    response = await devices.test_assigned_cdata_ssh_identity(FakeDB())

    assert response.status_code == 200
    assert b'"ok":true' in response.body
    assert seen["expected_host_key_fingerprint"].startswith("SHA256:")
    assert seen["encrypted_secret"] == "ciphertext"
