import re
import json
from typing import Optional, Union

from multi_swe_bench.harness.image import Config, File, Image
from multi_swe_bench.harness.instance import Instance, TestResult
from multi_swe_bench.harness.pull_request import PullRequest


class ImageDefault(Image):
    def __init__(self, pr: PullRequest, config: Config):
        self._pr = pr
        self._config = config

    @property
    def pr(self) -> PullRequest:
        return self._pr

    @property
    def config(self) -> Config:
        return self._config

    def dependency(self) -> str:
        return "python:3.9-slim"

    def image_prefix(self) -> str:
        return "envagent"

    def image_tag(self) -> str:
        return f"pr-{self.pr.number}"

    def workdir(self) -> str:
        return f"pr-{self.pr.number}"

    def files(self) -> list[File]:
        repo_name = self.pr.repo
        return [
            File(
                ".",
                "fix.patch",
                f"{self.pr.fix_patch}",
            ),
            File(
                ".",
                "test.patch",
                f"{self.pr.test_patch}",
            ),
            File(
                ".",
                "prepare.sh",
                """ls -la /home/opteryx
###ACTION_DELIMITER###
make compile
###ACTION_DELIMITER###
apt-get update && apt-get install -y make
###ACTION_DELIMITER###
make compile
###ACTION_DELIMITER###
export TERM=xterm && make compile
###ACTION_DELIMITER###
python -m uv pip install setuptools-rust
###ACTION_DELIMITER###
export TERM=xterm && make compile
###ACTION_DELIMITER###
apt-get update && apt-get install -y g++
###ACTION_DELIMITER###
export TERM=xterm && make compile
###ACTION_DELIMITER###
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source $HOME/.cargo/env
###ACTION_DELIMITER###
apt-get update && apt-get install -y curl
###ACTION_DELIMITER###
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && source $HOME/.cargo/env
###ACTION_DELIMITER###
export TERM=xterm && make compile
###ACTION_DELIMITER###
export TERM=xterm && make update
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes' > test_commands.sh
###ACTION_DELIMITER###
bash test_commands.sh
###ACTION_DELIMITER###
apt-get update && apt-get install -y valkey && service valkey start
###ACTION_DELIMITER###
apt-get update && apt-get install -y redis-server && service redis-server start
###ACTION_DELIMITER###
bash test_commands.sh
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey"' > test_commands.sh
###ACTION_DELIMITER###
bash test_commands.sh
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres"' > test_commands.sh
###ACTION_DELIMITER###
bash test_commands.sh
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not documentation"' > test_commands.sh
###ACTION_DELIMITER###
bash test_commands.sh
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not datastax and not minio and not mongo and not s3 and not documentation"' > test_commands.sh
###ACTION_DELIMITER###
echo -e 'export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not datastax and not minio and not mongo and not s3 and not documentation and not connector"' > test_commands.sh
###ACTION_DELIMITER###
bash test_commands.sh""",
            ),
            File(
                ".",
                "run.sh",
                """#!/bin/bash
cd /home/[[REPO_NAME]]
export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not datastax and not minio and not mongo and not s3 and not documentation and not connector"

""".replace("[[REPO_NAME]]", repo_name),
            ),
            File(
                ".",
                "test-run.sh",
                """#!/bin/bash
cd /home/[[REPO_NAME]]
if ! git -C /home/[[REPO_NAME]] apply --whitespace=nowarn /home/test.patch; then
    echo "Error: git apply failed" >&2
    exit 1  
fi
export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not datastax and not minio and not mongo and not s3 and not documentation and not connector"

""".replace("[[REPO_NAME]]", repo_name),
            ),
            File(
                ".",
                "fix-run.sh",
                """#!/bin/bash
cd /home/[[REPO_NAME]]
if ! git -C /home/[[REPO_NAME]] apply --whitespace=nowarn  /home/test.patch /home/fix.patch; then
    echo "Error: git apply failed" >&2
    exit 1  
fi
export MANUAL_TEST=1
python -m pytest -v -n auto --color=yes -k "not gcs and not valkey and not iceberg and not postgres and not cockroach and not mysql and not memcached and not redis and not duckdb and not datastax and not minio and not mongo and not s3 and not documentation and not connector"

""".replace("[[REPO_NAME]]", repo_name),
            ),
        ]

    def dockerfile(self) -> str:
        copy_commands = ""
        for file in self.files():
            copy_commands += f"COPY {file.name} /home/\n"

        dockerfile_content = """
# This is a template for creating a Dockerfile to test patches
# LLM should fill in the appropriate values based on the context

# Choose an appropriate base image based on the project's requirements - replace python:3.9-slim with actual base image
# For example: FROM ubuntu:**, FROM python:**, FROM node:**, FROM centos:**, etc.
FROM python:3.9-slim

## Set noninteractive
ENV DEBIAN_FRONTEND=noninteractive

# Install basic requirements
# For example: RUN apt-get update && apt-get install -y git
# For example: RUN yum install -y git
# For example: RUN apk add --no-cache git
RUN apt-get update && apt-get install -y git

# Ensure bash is available
RUN if [ ! -f /bin/bash ]; then         if command -v apk >/dev/null 2>&1; then             apk add --no-cache bash;         elif command -v apt-get >/dev/null 2>&1; then             apt-get update && apt-get install -y bash;         elif command -v yum >/dev/null 2>&1; then             yum install -y bash;         else             exit 1;         fi     fi

WORKDIR /home/
COPY fix.patch /home/
COPY test.patch /home/
RUN git clone https://github.com/mabel-dev/opteryx.git /home/opteryx

WORKDIR /home/opteryx
RUN git reset --hard
RUN git checkout {pr.base.sha}
"""
        dockerfile_content += f"""
{copy_commands}
"""
        return dockerfile_content.format(pr=self.pr)


@Instance.register("mabel-dev", "opteryx_2517_to_2462")
class OPTERYX_2517_TO_2462(Instance):
    def __init__(self, pr: PullRequest, config: Config, *args, **kwargs):
        super().__init__()
        self._pr = pr
        self._config = config

    @property
    def pr(self) -> PullRequest:
        return self._pr

    def dependency(self) -> Optional[Image]:
        return ImageDefault(self.pr, self._config)

    def run(self, run_cmd: str = "") -> str:
        if run_cmd:
            return run_cmd

        return "bash /home/run.sh"

    def test_patch_run(self, test_patch_run_cmd: str = "") -> str:
        if test_patch_run_cmd:
            return test_patch_run_cmd

        return "bash /home/test-run.sh"

    def fix_patch_run(self, fix_patch_run_cmd: str = "") -> str:
        if fix_patch_run_cmd:
            return fix_patch_run_cmd

        return "bash /home/fix-run.sh"

    def parse_log(self, log: str) -> TestResult:
        # Parse the log content and extract test execution results.
        passed_tests = set()  # Tests that passed successfully
        failed_tests = set()  # Tests that failed
        skipped_tests = set()  # Tests that were skipped
        import re

        # Remove ANSI escape codes
        log_clean = re.sub(r"\x1b\[[0-9;]*m", "", log)
        # Extract passed tests
        passed_pattern = re.compile(r"PASSED\s+(.*?)\s*$", re.MULTILINE)
        passed_tests.update(passed_pattern.findall(log_clean))
        # Extract failed tests
        failed_pattern = re.compile(r"FAILED\s+(.*?)\s+-\s", re.MULTILINE)
        failed_tests.update(failed_pattern.findall(log_clean))
        # Extract skipped tests
        skipped_pattern = re.compile(r"SKIPPED\s+(.*?)\s+-\s", re.MULTILINE)
        skipped_tests.update(skipped_pattern.findall(log_clean))
        parsed_results = {
            "passed_tests": passed_tests,
            "failed_tests": failed_tests,
            "skipped_tests": skipped_tests,
        }

        return TestResult(
            passed_count=len(passed_tests),
            failed_count=len(failed_tests),
            skipped_count=len(skipped_tests),
            passed_tests=passed_tests,
            failed_tests=failed_tests,
            skipped_tests=skipped_tests,
        )
