import codecs
import logging
import sys
from typing import List, Union

import colorlog
from sqlfluff.core import SQLBaseError


class LintReporter:

    def __init__(self):
        self.sql_linter_log = LintReporter._create_logger(logging.DEBUG)

    def _get_extra_default_dict(self):
        return {"pos_info": "", "description": "", "warn": "", "pass": ""}

    @staticmethod
    def _create_logger(log_level: Union[int, str]):
        logger = logging.getLogger('linter_logger')
        logger.setLevel(log_level)
        info_formater = colorlog.ColoredFormatter(
            fmt=(
                "%(white)s%(message)s"
                "%(red)s%(warn)s "
                "%(green)s%(pass)s "
                "%(blue)s%(pos_info)s "
                "%(white)s%(description)s "
            )
        )
        python_version = sys.version_info
        if python_version.major == 3 and python_version.minor == 6:
            sys.stdout = codecs.getwriter("utf-8")(sys.stdout.detach())
        elif hasattr(sys.stdout, 'reconfigure'):
            sys.stdout.reconfigure(encoding='utf-8')
        handler = logging.StreamHandler(sys.stdout)
        handler.setFormatter(info_formater)
        for existing_handler in logger.handlers:
            logger.removeHandler(existing_handler)
        logger.addHandler(handler)
        return logger

    def report_violation(self, violation: SQLBaseError, step_start_line=0):
        pos_info = "L: {} | P: {}: | {}  :".format(violation.line_no + step_start_line,
                                                   violation.line_pos, violation.rule_code())
        extra_dict = self._get_extra_default_dict()
        extra_dict["pos_info"] = pos_info
        extra_dict["description"] = violation.desc()
        self.sql_linter_log.info("", extra=extra_dict)

    def report_list_of_violations(self, lint_result: List[SQLBaseError], step_start_line=0):
        if len(lint_result) > 0:
            self.report_warning("Fail")
            for violation in lint_result:
                self.report_violation(violation, step_start_line)
        else:
            self.report_pass("Pass")

    def report_message(self, message):
        self.sql_linter_log.info(message, extra=self._get_extra_default_dict())

    def report_warning(self, warning: str):
        extra_dict = self._get_extra_default_dict()
        extra_dict["warn"] = warning
        self.sql_linter_log.warning("", extra=extra_dict)

    def report_pass(self, pass_info: str):
        extra_dict = self._get_extra_default_dict()
        extra_dict["pass"] = pass_info
        self.sql_linter_log.warning("", extra=extra_dict)
