"""Validation rules for script parameters (type coercion, regex pattern)."""

from __future__ import annotations

import pytest
from fastapi import HTTPException

from media_server.config import ScriptParameterConfig
from media_server.routes.scripts import _validate_params


def _defs(**kwargs) -> dict[str, ScriptParameterConfig]:
    return {name: ScriptParameterConfig(**spec) for name, spec in kwargs.items()}


def test_unknown_param_rejected():
    with pytest.raises(HTTPException) as ei:
        _validate_params({"x": "1"}, _defs())
    assert ei.value.status_code == 400
    assert "Unknown" in ei.value.detail


def test_missing_required_rejected():
    defs = _defs(name={"type": "string", "required": True})
    with pytest.raises(HTTPException, match="missing"):
        _validate_params({}, defs)


def test_integer_coercion_and_bounds():
    defs = _defs(volume={"type": "integer", "min": 0, "max": 100})
    out = _validate_params({"volume": "42"}, defs)
    assert out == {"SCRIPT_PARAM_VOLUME": "42"}

    with pytest.raises(HTTPException, match="<="):
        _validate_params({"volume": 200}, defs)
    with pytest.raises(HTTPException, match=">="):
        _validate_params({"volume": -1}, defs)
    with pytest.raises(HTTPException, match="integer"):
        _validate_params({"volume": "not-a-number"}, defs)


def test_boolean_coercion():
    defs = _defs(flag={"type": "boolean"})
    assert _validate_params({"flag": "true"}, defs) == {"SCRIPT_PARAM_FLAG": "True"}
    assert _validate_params({"flag": "no"}, defs) == {"SCRIPT_PARAM_FLAG": "False"}
    with pytest.raises(HTTPException, match="boolean"):
        _validate_params({"flag": "maybe"}, defs)


def test_select_rejects_non_option():
    defs = _defs(mode={"type": "select", "options": ["a", "b", "c"]})
    assert _validate_params({"mode": "a"}, defs) == {"SCRIPT_PARAM_MODE": "a"}
    with pytest.raises(HTTPException, match="must be one of"):
        _validate_params({"mode": "z"}, defs)


def test_pattern_enforced_on_string():
    """Regex pattern is the defence against shell metachars in shell=true scripts."""
    defs = _defs(host={"type": "string", "pattern": r"^[a-z0-9.\-]+$"})
    assert _validate_params({"host": "example.com"}, defs) == {"SCRIPT_PARAM_HOST": "example.com"}
    with pytest.raises(HTTPException, match="pattern"):
        _validate_params({"host": "evil & calc.exe"}, defs)
    with pytest.raises(HTTPException, match="pattern"):
        _validate_params({"host": "$(rm -rf /)"}, defs)


def test_pattern_can_disallow_empty():
    defs = _defs(host={"type": "string", "pattern": r"^[a-z]+$"})
    with pytest.raises(HTTPException, match="pattern"):
        _validate_params({"host": ""}, defs)


def test_invalid_pattern_in_config_fails_closed():
    defs = _defs(host={"type": "string", "pattern": r"["})  # unmatched bracket
    with pytest.raises(HTTPException) as ei:
        _validate_params({"host": "x"}, defs)
    assert ei.value.status_code == 500
