# Copyright 2024-2026 The DNS-AID Authors
# SPDX-License-Identifier: Apache-2.0

"""Tests for CLI onboarding: backends registry, _get_backend(), doctor, init."""

from __future__ import annotations

import os
from unittest.mock import patch

import pytest
from typer.testing import CliRunner

from dns_aid.cli.backends import ALL_BACKEND_NAMES, BACKEND_REGISTRY, detect_backend
from dns_aid.cli.main import app

runner = CliRunner()


# ── Backend Registry ───────────────────────────────────────────────


class TestBackendRegistry:
    def test_all_backends_present(self):
        assert set(ALL_BACKEND_NAMES) == {"route53", "cloudflare", "infoblox", "ddns", "mock"}

    def test_real_backends_have_required_env(self):
        for name in ("route53", "cloudflare", "infoblox", "ddns"):
            info = BACKEND_REGISTRY[name]
            assert info.required_env, f"{name} should have required_env"

    def test_mock_has_no_required_env(self):
        assert not BACKEND_REGISTRY["mock"].required_env

    def test_all_have_display_name(self):
        for name, info in BACKEND_REGISTRY.items():
            assert info.display_name, f"{name} missing display_name"


# ── detect_backend() ──────────────────────────────────────────────


class TestDetectBackend:
    def test_no_env_returns_none(self):
        with patch.dict(os.environ, {}, clear=True):
            assert detect_backend() is None

    def test_detects_route53(self):
        env = {
            "AWS_ACCESS_KEY_ID": "AKIA...",
            "AWS_SECRET_ACCESS_KEY": "secret",
        }
        with patch.dict(os.environ, env, clear=True):
            assert detect_backend() == "route53"

    def test_detects_cloudflare(self):
        env = {"CLOUDFLARE_API_TOKEN": "cf-token"}
        with patch.dict(os.environ, env, clear=True):
            assert detect_backend() == "cloudflare"

    def test_detects_infoblox(self):
        env = {"INFOBLOX_API_KEY": "ib-key"}
        with patch.dict(os.environ, env, clear=True):
            assert detect_backend() == "infoblox"

    def test_detects_ddns(self):
        env = {"DDNS_SERVER": "ns1.example.com"}
        with patch.dict(os.environ, env, clear=True):
            assert detect_backend() == "ddns"

    def test_multiple_raises(self):
        env = {
            "AWS_ACCESS_KEY_ID": "AKIA...",
            "AWS_SECRET_ACCESS_KEY": "secret",
            "CLOUDFLARE_API_TOKEN": "cf-token",
        }
        with patch.dict(os.environ, env, clear=True):
            with pytest.raises(ValueError, match="Multiple backends"):
                detect_backend()


# ── _get_backend() (improved) ─────────────────────────────────────


class TestGetBackendImproved:
    def test_env_var_detection(self):
        """DNS_AID_BACKEND env var is respected."""
        env = {"DNS_AID_BACKEND": "mock"}
        with patch.dict(os.environ, env, clear=True):
            result = runner.invoke(app, ["list", "example.com"])
            # mock backend doesn't need credentials; it will run but find no records
            assert result.exit_code == 0

    def test_explicit_backend_mock(self):
        result = runner.invoke(app, ["list", "--backend", "mock", "example.com"])
        assert result.exit_code == 0

    def test_unknown_backend_error(self):
        result = runner.invoke(app, ["list", "--backend", "nonexistent", "example.com"])
        assert result.exit_code != 0
        assert "Unknown backend" in result.output or "Unknown backend" in (result.stderr or "")

    def test_missing_env_vars_shows_guidance(self):
        """When backend is set but creds are missing, show actionable help."""
        env = {"DNS_AID_BACKEND": "route53"}
        with patch.dict(os.environ, env, clear=True):
            # Patch out boto3 import to not fail on ImportError first
            with patch.dict("sys.modules", {"boto3": None}):
                result = runner.invoke(app, ["list", "example.com"])
                assert result.exit_code != 0

    def test_no_backend_configured_shows_init_hint(self):
        """When nothing is configured, suggest dns-aid init."""
        with patch.dict(os.environ, {}, clear=True):
            result = runner.invoke(app, ["list", "example.com"])
            assert result.exit_code != 0
            assert "init" in result.output or "init" in (result.stderr or "")

    def test_auto_detect_mock_env(self):
        """Mock via DNS_AID_BACKEND works end to end."""
        env = {"DNS_AID_BACKEND": "mock"}
        with patch.dict(os.environ, env, clear=True):
            result = runner.invoke(app, ["list", "example.com"])
            assert result.exit_code == 0


# ── dns-aid doctor ─────────────────────────────────────────────────


class TestDoctor:
    def test_doctor_runs(self):
        result = runner.invoke(app, ["doctor"])
        assert result.exit_code == 0
        assert "Core" in result.output
        assert "dns-aid" in result.output

    def test_doctor_shows_python_version(self):
        result = runner.invoke(app, ["doctor"])
        assert "Python" in result.output

    def test_doctor_shows_summary(self):
        result = runner.invoke(app, ["doctor"])
        assert "Summary" in result.output


# ── dns-aid init ───────────────────────────────────────────────────


class TestInit:
    def test_init_help(self):
        result = runner.invoke(app, ["init", "--help"])
        assert result.exit_code == 0
        assert "wizard" in result.output.lower() or "setup" in result.output.lower()

    def test_init_discover_quickstart(self):
        """Choosing option 0 (discover-only) shows quickstart."""
        result = runner.invoke(app, ["init"], input="0\nn\n")
        assert result.exit_code == 0
        assert "discover" in result.output.lower()
