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

import pytest
from pydantic import BaseModel

from bananalyzer.data.schemas import Example
from bananalyzer.hooks import BananalyzerPytestPlugin
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(viewport={{ 'width': 1280, 'height': 1024 }})
        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,
) -> Tuple[int, Path]:
    """
    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"--junitxml={str(report_path)}"]
        )

        return pytest.main(args, plugins=[(BananalyzerPytestPlugin())]), report_path
