# coding:utf-8

import html
import inspect
import traceback

from makit.lib import ospath

FRAME_TEMPLATE = """
<div class="frame">
    <div class="frame-title">
        File {frame_filename}, line <i>{frame_lineno}</i>, in <b>{frame_name}</b>
    </div>
    <div class="source-code">{code_context}</div>
</div>
"""

LINE = """
<div class="source-line {error}">
    <span class="lineno">{lineno}</span> <span class="line-text">{line}</span>
</div>
"""


class ErrorFormatter:
    """

    """

    def format(self, exc) -> str:
        """
        格式化异常
        :param exc:
        :return:
        """
        raise NotImplementedError()


def fmt_text(exc):
    """"""
    return "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))


def fmt_html(exc):
    """"""
    return generate_html(exc)


def generate_html(exc, limit=7):
    traceback_obj = traceback.TracebackException.from_exception(
        exc, capture_locals=True
    )
    exc_html = ""
    exc_traceback = exc.__traceback__
    if exc_traceback is not None:
        frames = inspect.getinnerframes(exc_traceback, limit)
        for frame in reversed(frames):
            exc_html += generate_frame_html(frame)
    error = (
        f"{html.escape(traceback_obj.exc_type.__name__)}: "
        f"{html.escape(str(traceback_obj))}"
    )
    template_file = ospath.join(__file__, '../../static/error-template.html')
    with open(template_file, 'r', encoding='utf-8') as fr:
        template = fr.read()
    content = template.replace('{error}', error).replace('{exc_html}', exc_html)
    # with open('./error.html', 'w', encoding='utf-8') as fw:
    #     fw.write(content)
    return content


def generate_frame_html(frame):
    code_context = "".join(
        format_line(
            index, line, frame.lineno, frame.index
        )
        for index, line in enumerate(frame.code_context or [])
    )

    values = {
        "frame_filename": html.escape(frame.filename),
        "frame_lineno": frame.lineno,
        "frame_name": html.escape(frame.function),
        "code_context": code_context,
    }
    return FRAME_TEMPLATE.format(**values)


def format_line(
        index: int, line: str, frame_lineno: int, frame_index: int
) -> str:
    values = {
        "line": html.escape(line).replace(" ", "&nbsp"),
        "lineno": (frame_lineno - frame_index) + index,
        'error': 'error' if index == frame_index else ''
    }

    return LINE.format(**values)
