from autotester.types import TestErrorType
import os
from pathlib import Path
import logging
from .models.provider_factory import ProviderFactory, ProviderType

logger = logging.getLogger("autotester")

PROMPT = """
You are given source code, a test, and an error log generated by running the test.
You are tasked with determining if the error is caused by the test, by a bug in the code being tested, or by the test settings. You can use markdown.

* Think the problem through. Wrap your reasoning in  the <explanation> block to explain your reasoning.
* Was this error caused by how the test was written or its imports or can a different test be written? If so, end your reply with /test
* Was this error caused by how the code that is being tested is written? If so, end your reply with /bug
* Was this error caused by misconfiguration of the test settings and there is no possibility of writing a different test to cover a different scenario? If so, end your reply with /settings

{log}
"""


class AnalyzeError:

    def __init__(
        self,
        source_code: str,
        source_code_path: Path,
        tests: str,
        tests_path: Path,
        error: str,
    ) -> None:
        self.error = error
        self.log = f"""

---SOURCE CODE: {source_code_path}---
{source_code}

---TESTS: {tests_path}---
{tests}

---ERROR LOG---
{error}
"""
        provider_type = os.getenv("AUTOTESETER_PROVIDER", "openai")
        self.provider = ProviderFactory.get_provider(ProviderType(provider_type))

    def analyze(self) -> tuple[TestErrorType, str | None]:
        """
        Analyze the console error using LLM to determine the error type.

        Returns:
            ErrorType: The categorized error type
        """
        if self.error == "exit status 1":
            return TestErrorType.TEST, self.error

        response = self.provider.create_chat_completion(
            messages=[
                {
                    "role": "user",
                    "content": PROMPT.format(log=self.log),
                }
            ],
            max_completion_tokens=100000,
        )
        first_res = response.choices[0].message.content
        return self._parse_response(str(first_res))

    def _parse_response(self, response: str) -> tuple[TestErrorType, str | None]:
        """
        Parse the LLM response to determine the error type and explanation.

        Args:
            response (str): The LLM response string

        Returns:
            tuple[TestErrorType, str | None]: The parsed error type and explanation

        Note:
            Extracts explanation from <explanation> tags and determines error type
            from the /test, /bug, or /settings tag at the end of the response.
        """
        explanation = None
        if "<explanation>" in response:
            explanation_parts = response.split("<explanation>")
            if len(explanation_parts) > 1:
                explanation = explanation_parts[1].split("</explanation>")[0].strip()

        if "<error_explanation>" in response and explanation is None:
            error_explanation_parts = response.split("<error_explanation>")
            if len(error_explanation_parts) > 1:
                explanation = (
                    error_explanation_parts[1].split("</error_explanation>")[0].strip()
                )

        tag = None

        if "/settings" in response:
            tag = TestErrorType.SETTINGS
        elif "/test" in response:
            tag = TestErrorType.TEST
        elif "/bug" in response:
            tag = TestErrorType.BUG

        if tag is None:
            if "[test]" in response:
                tag = TestErrorType.TEST
            elif "[bug]" in response:
                tag = TestErrorType.BUG
            elif "[settings]" in response:
                tag = TestErrorType.SETTINGS
        if tag is None:
            for line in response.splitlines():
                line = line.strip()
                if line == "(test)":
                    tag = TestErrorType.TEST
                    break
                elif line == "(bug)":
                    tag = TestErrorType.BUG
                    break
                elif line == "(settings)":
                    tag = TestErrorType.SETTINGS
                    break

        if tag is None:
            raise ValueError(f"Could not parse error type from response: {response}")

        return tag, explanation
