"""Unit tests for the FaaS request router in products/faas/handler.py."""

import importlib
import json
from unittest.mock import MagicMock

import pytest

# products/faas/__init__.py rebinds the package attribute `handler` to the
# function, shadowing the submodule — load the module explicitly via importlib.
h = importlib.import_module("products.faas.handler")

pytestmark = pytest.mark.unit


@pytest.fixture
def ctx():
    c = MagicMock()
    c.get_project.return_value = "proj"
    c.get_product_domain.return_value = "domain"
    c.get_accesstoken.return_value = "tok"
    c.get_secret.return_value = "sval"
    return c


@pytest.fixture(autouse=True)
def patch_api_function(mocker):
    mock = mocker.patch.object(h, "api_function")
    mock.render_markdown.return_value = "<html>md</html>"
    mock.make_common_response.return_value = ("ok", 200)
    return mock


def test_create_response_shape():
    r = h.create_response("body", 201, h.CONTENT_TYPE_HTML)
    assert r == {"body": "body", "status": 201, "headers": {"Content-Type": h.CONTENT_TYPE_HTML}}


def test_root_ok(ctx):
    r = h.handler({"path": "/"}, ctx)
    assert r["status"] == 200 and r["headers"]["Content-Type"] == h.CONTENT_TYPE_HTML


def test_root_file_not_found(ctx, patch_api_function):
    patch_api_function.render_markdown.side_effect = FileNotFoundError
    r = h.handler({"path": "/"}, ctx)
    assert r["status"] == 404


def test_root_internal_error(ctx, patch_api_function):
    patch_api_function.render_markdown.side_effect = RuntimeError("boom")
    r = h.handler({"path": "/"}, ctx)
    assert r["status"] == 500


def test_function_test_ui_invalid_env(ctx, monkeypatch):
    monkeypatch.setenv("FUNC_ENVIRONMENT", "nope")
    r = h.handler({"path": "/function_test_ui"}, ctx)
    assert r["status"] == 400


def test_function_test_ui_valid(ctx, monkeypatch, mocker):
    monkeypatch.setenv("FUNC_ENVIRONMENT", "prod")
    monkeypatch.setenv("FUNC_NAME", "fn")
    mocker.patch.object(h, "render_template", return_value="<ui>")
    r = h.handler({"path": "/function_test_ui"}, ctx)
    assert r["status"] == 200 and r["body"] == "<ui>"


def test_access_token_ok(ctx):
    r = h.handler({"path": "/get_access_token"}, ctx)
    assert json.loads(r["body"])["token"] == "tok"


def test_access_token_error(ctx):
    ctx.get_accesstoken.side_effect = RuntimeError("x")
    r = h.handler({"path": "/get_access_token"}, ctx)
    assert r["status"] == 500


def test_test_timeout(ctx):
    r = h.handler({"path": "/test_timeout", "query": {"sleep": "0"}}, ctx)
    assert json.loads(r["body"])["success"] is True


def test_test_timeout_bad_value(ctx):
    r = h.handler({"path": "/test_timeout", "query": {"sleep": "abc"}}, ctx)
    assert r["status"] == 500


def test_env_request(ctx, monkeypatch):
    monkeypatch.setenv("MY_ENV_KEY", "val")
    r = h.handler({"path": "/env/MY_ENV_KEY"}, ctx)
    assert json.loads(r["body"])["value"] == "val"


def test_send_request_no_host(ctx):
    r = h.handler({"path": "/send_request", "query": {}}, ctx)
    assert r["status"] == 400


def test_send_request_ok(ctx, mocker):
    mocker.patch.object(h.requests, "get", return_value=MagicMock())
    r = h.handler({"path": "/send_request", "query": {"host": "http://x"}}, ctx)
    assert json.loads(r["body"])["success"] is True


def test_send_request_failure(ctx, mocker):
    mocker.patch.object(h.requests, "get", side_effect=h.requests.RequestException("down"))
    r = h.handler({"path": "/send_request", "query": {"host": "http://x"}}, ctx)
    assert r["status"] == 500


def test_secret_manager_ok(ctx):
    r = h.handler({"path": "/secret_manager/g/x/s", "query": {"version": "1"}}, ctx)
    assert json.loads(r["body"])["secret"] == "sval"


def test_secret_manager_error(ctx):
    ctx.get_secret.side_effect = RuntimeError("x")
    r = h.handler({"path": "/secret_manager/g/x/s"}, ctx)
    assert r["status"] == 500


def test_static_file_ok(ctx, tmp_path, monkeypatch):
    f = tmp_path / "a.js"
    f.write_text("console.log(1)")
    monkeypatch.chdir(tmp_path)
    r = h.handler({"path": "/a.js"}, ctx)
    assert r["status"] == 200 and r["headers"]["Content-Type"] == "application/javascript"


def test_static_file_not_found(ctx):
    r = h.handler({"path": "/does-not-exist.png"}, ctx)
    assert r["status"] == 404


def test_determine_content_type():
    assert h.determine_content_type("a.css") == "text/css"
    assert h.determine_content_type("a.png") == "image/png"
    assert h.determine_content_type("a.txt") == h.CONTENT_TYPE_TEXT


def test_get_elapsed_time(ctx):
    r = h.handler({"path": "/get_elapsed_time"}, ctx)
    assert json.loads(r["body"])["value"] >= 0


def test_test_path(ctx):
    r = h.handler({"path": "/test"}, ctx)
    assert r == ("ok", 200)


def test_markdown_file_ok(ctx):
    r = h.handler({"path": "/guide.md"}, ctx)
    assert r["status"] == 200


def test_markdown_file_not_found(ctx, patch_api_function):
    patch_api_function.render_markdown.side_effect = FileNotFoundError
    r = h.handler({"path": "/missing.md"}, ctx)
    assert r["status"] == 404


def test_markdown_non_md_path(ctx):
    r = h.handler({"path": "/weird"}, ctx)
    assert r["status"] == 400
