from typing import List

from dev_aci.utils.logger import oh_aci_logger as logger
from dev_aci.utils.shell import run_shell_cmd

from ..base import BaseLinter, LintResult


def python_compile_lint(fname: str) -> list[LintResult]:
    try:
        with open(fname, 'r') as f:
            code = f.read()
        compile(code, fname, 'exec')  # USE TRACEBACK BELOW HERE
        return []
    except SyntaxError as err:
        err_lineno = getattr(err, 'end_lineno', err.lineno)
        err_offset = getattr(err, 'end_offset', err.offset)
        if err_offset and err_offset < 0:
            err_offset = err.offset
        return [
            LintResult(
                file=fname, line=err_lineno, column=err_offset or 1, message=err.msg
            )
        ]


def flake_lint(filepath: str) -> list[LintResult]:
    fatal = 'F821,F822,F831,E112,E113,E999,E902'
    flake8_cmd = f'flake8 --select={fatal} --isolated {filepath}'

    try:
        cmd_outputs = run_shell_cmd(flake8_cmd, truncate_after=None)[1]
    except FileNotFoundError:
        return []
    results: list[LintResult] = []
    if not cmd_outputs:
        return results
    for line in cmd_outputs.splitlines():
        parts = line.split(':')
        if len(parts) >= 4:
            _msg = parts[3].strip()
            if len(parts) > 4:
                _msg += ': ' + parts[4].strip()

            try:
                line_num = int(parts[1])
            except ValueError as e:
                logger.warning(
                    f'Error parsing flake8 output for line: {e}. Parsed parts: {parts}. Skipping...'
                )
                continue

            try:
                column_num = int(parts[2])
            except ValueError as e:
                column_num = 1
                _msg = (
                    parts[2].strip() + ' ' + _msg
                )  # add the unparsed message to the original message
                logger.warning(
                    f'Error parsing flake8 output for column: {e}. Parsed parts: {parts}. Using default column 1.'
                )

            results.append(
                LintResult(
                    file=filepath,
                    line=line_num,
                    column=column_num,
                    message=_msg,
                )
            )
    return results


class PythonLinter(BaseLinter):
    @property
    def supported_extensions(self) -> List[str]:
        return ['.py']

    def lint(self, file_path: str) -> list[LintResult]:
        error = flake_lint(file_path)
        if not error:
            error = python_compile_lint(file_path)
        return error

    def compile_lint(self, file_path: str, code: str) -> List[LintResult]:
        try:
            compile(code, file_path, 'exec')
            return []
        except SyntaxError as e:
            return [
                LintResult(
                    file=file_path,
                    line=e.lineno,
                    column=e.offset,
                    message=str(e),
                    rule='SyntaxError',
                )
            ]
