from __future__ import annotations

import os
from unittest.mock import MagicMock, patch
import pytest

from actions.ssl import (
    handle_ssl_generate,
    handle_ssl_renew,
    handle_ssl_delete,
)


def test_domain_validation_blocks_bad_characters():
    bad_domains = [
        "domain; rm -rf /",
        "../etc/passwd",
        "domain.com/path",
        "domain com",
        "domain$",
        "a" * 254
    ]
    for dom in bad_domains:
        # generate
        res = handle_ssl_generate({"domain": dom, "email": "test@example.com"})
        assert res["success"] is False
        assert res["error"] == "invalid_domain"

        # renew
        res = handle_ssl_renew({"domain": dom})
        assert res["success"] is False
        assert res["error"] == "invalid_domain"

        # delete
        res = handle_ssl_delete({"domain": dom})
        assert res["success"] is False
        assert res["error"] == "invalid_domain"


def test_email_validation_blocks_bad_emails():
    bad_emails = [
        "invalidemail",
        "",
        "test@",
        "@example.com",
        None
    ]
    for email in bad_emails:
        res = handle_ssl_generate({"domain": "example.com", "email": email})
        assert res["success"] is False
        assert res["error"] == "invalid_email"


@patch("shutil.which", return_value=None)
def test_certbot_not_found_returns_error(mock_which):
    # generate
    res = handle_ssl_generate({"domain": "example.com", "email": "test@example.com"})
    assert res["success"] is False
    assert res["error"] == "certbot_not_found"

    # renew
    res = handle_ssl_renew({"domain": "example.com"})
    assert res["success"] is False
    assert res["error"] == "certbot_not_found"

    # delete
    with patch("os.path.exists", return_value=True):
        res = handle_ssl_delete({"domain": "example.com"})
        assert res["success"] is False
        assert res["error"] == "certbot_not_found"


@patch("shutil.which", return_value="/usr/bin/certbot")
@patch("subprocess.run")
def test_ssl_generate_success(mock_run, mock_which):
    mock_run.return_value = MagicMock(returncode=0, stdout="success generate", stderr="")
    
    with patch("os.path.exists", return_value=True):
        res = handle_ssl_generate({"domain": "example.com", "email": "test@example.com"})
        
    assert res["success"] is True
    assert "success generate" in res["stdout"]
    assert res["cert_path"] == "/etc/letsencrypt/live/example.com/fullchain.pem"
    
    mock_run.assert_called_once_with(
        [
            "certbot", "certonly",
            "--nginx",
            "-d", "example.com",
            "--non-interactive",
            "--agree-tos",
            "-m", "test@example.com",
            "--no-eff-email"
        ],
        capture_output=True,
        text=True,
        shell=False,
        timeout=120
    )


@patch("shutil.which", return_value="/usr/bin/certbot")
@patch("subprocess.run")
def test_ssl_renew_success(mock_run, mock_which):
    mock_run.return_value = MagicMock(returncode=0, stdout="success renew", stderr="")
    
    res = handle_ssl_renew({"domain": "example.com"})
    
    assert res["success"] is True
    assert "success renew" in res["stdout"]
    
    mock_run.assert_called_once_with(
        [
            "certbot", "renew",
            "--cert-name", "example.com",
            "--non-interactive"
        ],
        capture_output=True,
        text=True,
        shell=False,
        timeout=120
    )


@patch("shutil.which", return_value="/usr/bin/certbot")
@patch("os.path.exists", return_value=False)
def test_ssl_delete_not_found(mock_exists, mock_which):
    res = handle_ssl_delete({"domain": "example.com"})
    assert res["success"] is False
    assert res["error"] == "cert_not_found"


@patch("shutil.which", return_value="/usr/bin/certbot")
@patch("subprocess.run")
def test_ssl_delete_success(mock_run, mock_which):
    mock_run.return_value = MagicMock(returncode=0, stdout="success delete", stderr="")
    
    with patch("os.path.exists", return_value=True):
        res = handle_ssl_delete({"domain": "example.com"})
        
    assert res["success"] is True
    assert "success delete" in res["stdout"]
    
    mock_run.assert_called_once_with(
        [
            "certbot", "delete",
            "--cert-name", "example.com",
            "--non-interactive"
        ],
        capture_output=True,
        text=True,
        shell=False,
        timeout=120
    )
