import os
import tempfile
from pathlib import Path
from typing import Awaitable, Callable, List

import pytest
from pydantic import BaseModel

from bananalyzer.data.schemas import Example
from bananalyzer.schema import AgentRunnerClass, PytestArgs

TestType = Callable[[], Awaitable[None]]


class BananalyzerTest(BaseModel):
    code: str
    example: Example


def create_test_file(
    tests: List[BananalyzerTest],
    prefix: str,
    cache_dir: Path,
    runner: AgentRunnerClass,
    headless: bool,
    single_browser_instance: bool,
) -> str:
    with tempfile.NamedTemporaryFile(
        mode="w+",
        delete=False,
        prefix=prefix,
        suffix=".py",
        dir=cache_dir,
    ) as f:
        # noinspection PyUnresolvedReferences
        f.write(
            f"""
import pytest
import pytest_asyncio
import asyncio

from bananalyzer.data.examples import get_example_by_url
from playwright.async_api import async_playwright


@pytest.fixture(scope="session")
def event_loop():
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="session")
def agent():
    import importlib.util
    import sys
    from pathlib import Path
    import pprint


    path = Path("{runner.class_path}")
    module_name = path.stem
    spec = importlib.util.spec_from_file_location(module_name, str(path))
    module = importlib.util.module_from_spec(spec)

    sys.modules[module_name] = module
    spec.loader.exec_module(module)
    print(f"Loaded agent module", module)

    agent_ = getattr(module, '{runner.class_name}')()
    yield agent_


@pytest_asyncio.fixture(scope="{'session' if single_browser_instance else 'class'}")
async def page():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless={headless})
        context = await browser.new_context()
        page = await context.new_page()
        yield page
        await browser.close()

"""
        )
        for test_content in tests:
            f.write(f"{test_content.code}\n\n")

    return f.name


def run_tests(
    tests: List[BananalyzerTest],
    runner: AgentRunnerClass,
    pytest_args: PytestArgs,
    headless: bool = False,
    single_browser_instance: bool = False,
) -> int:
    """
    Create temporary test files based on intent, run them, and then delete them
    """
    intents = {test.example.type for test in tests}
    intent_separated_tests = [
        [test for test in tests if test.example.type == intent] for intent in intents
    ]

    cache_dir = Path(os.getcwd()) / ".banana_cache"
    cache_dir.mkdir(exist_ok=True)
    with open(cache_dir / ".gitignore", "w") as f:
        f.write("# Generated by bananalyzer automatically\n*")

    with tempfile.TemporaryDirectory(dir=cache_dir) as temp_dir:
        temp_path = Path(temp_dir)

        test_file_names = [
            create_test_file(
                tests,
                f"{tests[0].example.type}_intent_",
                temp_path,
                runner,
                headless,
                single_browser_instance,
            )
            for tests in intent_separated_tests
        ]

        report_path = cache_dir / f"{temp_path.stem}_report.html"
        args = (
            test_file_names
            + (["-s"] if pytest_args.s else [])
            + ([f"-n {pytest_args.n}"] if pytest_args.n else [])
            + (["-q"] if pytest_args.q else ["-vvv"])
            + [f"--html={str(report_path)}"]
        )

        return pytest.main(args)
