"""Request validation and test instrumentation utilities."""

from __future__ import annotations

from typing import TYPE_CHECKING

import libcst as cst
from ninja.errors import HttpError

from aiservice.common.markdown_utils import split_markdown_code
from aiservice.common_utils import parse_python_version, validate_trace_id
from core.languages.python.optimizer.context_utils.context_helpers import is_multi_context
from core.languages.python.testgen.instrumentation.instrument_new_tests import instrument_test_source
from core.languages.python.testgen.models import TestingMode

if TYPE_CHECKING:
    from core.shared.testgen_models import TestGenSchema


def instrument_tests(
    generated_test_source: str, data: TestGenSchema, python_version: tuple[int, int, int]
) -> tuple[str | None, str | None]:
    # instrument_test_source() already applies isort via format_and_float_to_top()
    behavior_result = instrument_test_source(
        test_source=generated_test_source,
        function_to_optimize=data.function_to_optimize,
        helper_function_names=data.helper_function_names or [],
        module_path=data.module_path,
        test_module_path=data.test_module_path,
        test_framework=data.test_framework,
        test_timeout=data.test_timeout,
        python_version=python_version,
        mode=TestingMode.BEHAVIOR,
    )
    perf_result = instrument_test_source(
        test_source=generated_test_source,
        function_to_optimize=data.function_to_optimize,
        helper_function_names=data.helper_function_names or [],
        module_path=data.module_path,
        test_module_path=data.test_module_path,
        test_framework=data.test_framework,
        test_timeout=data.test_timeout,
        python_version=python_version,
        mode=TestingMode.PERFORMANCE,
    )

    return behavior_result, perf_result


def validate_source_code(source_code: str) -> None:
    if is_multi_context(source_code):
        for scoped_code in split_markdown_code(source_code).values():
            parsed = cst.parse_module(scoped_code)
            if not parsed.body:
                msg = "Source code is empty or invalid"
                raise SyntaxError(msg)
    else:
        parsed = cst.parse_module(source_code)
        if not parsed.body:
            msg = "Source code is empty or invalid"
            raise SyntaxError(msg)


def validate_request_data(data: TestGenSchema) -> tuple[int, int, int]:
    if data.test_framework not in ["unittest", "pytest"]:
        raise HttpError(400, "Invalid test framework. We only support unittest and pytest.")
    if not data.function_to_optimize:
        raise HttpError(400, "Invalid function to optimize. It is empty.")
    if not validate_trace_id(data.trace_id):
        raise HttpError(400, "Invalid trace ID. Please provide a valid UUIDv4.")
    if not data.python_version:
        raise HttpError(400, "Python version is required.")

    try:
        python_version = parse_python_version(data.python_version)
    except ValueError:
        raise HttpError(400, "Invalid Python version, it should look like 3.x.x. We only support Python 3.9 and above.")  # noqa: B904

    try:
        validate_source_code(data.source_code_being_tested)
    except SyntaxError:
        raise HttpError(400, "Invalid source code. It is not valid Python code.")  # noqa: B904

    return python_version
