"""IPythonKernel based on pystata"""

# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/14_kernel.ipynb.

# %% auto 0
__all__ = ['PyStataKernel', 'print_stata_error']

# %% ../nbs/14_kernel.ipynb 4
from .config import Config
from .misc_utils import print_red
from .stata_more import user_expression
from .inspect import get_inspect
from .stata_session import StataSession
from .completions import CompletionsManager
from .cell import Cell
import nbstata # for __version__
from fastcore.basics import patch_to
from ipykernel.ipkernel import IPythonKernel

# %% ../nbs/14_kernel.ipynb 6
class PyStataKernel(IPythonKernel):
    """A jupyter kernel based on pystata"""
    implementation = 'nbstata'
    implementation_version = nbstata.__version__
    language_info = {
        'name': 'stata',
        'version': '17',
        'mimetype': 'text/x-stata',
        'file_extension': '.do',
    }
    banner = "nbstata: a Jupyter kernel for Stata based on pystata"
    help_links = [
        {
            "text": "Stata Documentation",
            "url": "https://www.stata.com/features/documentation/",
        },
        {
            "text": "nbstata Help",
            "url": "https://hugetim.github.io/nbstata/",
        },
    ]

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.stata_ready = False
        self.ipydatagrid_height_set = False
        self.shell.execution_count = 0
        self.inspect_output = "Stata not yet initialized."
        self.nbstata_config = Config()
        self.stata_session = StataSession()
        self.completions = CompletionsManager(self.stata_session)
        self.inspect_output = ""

# %% ../nbs/14_kernel.ipynb 8
@patch_to(PyStataKernel)
def init_session(self):
    self.nbstata_config.process_config_file()
    self.nbstata_config.init_stata()
    self.stata_ready = True

# %% ../nbs/14_kernel.ipynb 9
def _stata_error_reply(ename, evalue, execution_count=None):
    reply_content = {
        'status': "error",
        "traceback": [],
        "ename": ename,
        "evalue": evalue,
    }
    if execution_count is not None:
        reply_content['execution_count'] = execution_count
    return reply_content

# %% ../nbs/14_kernel.ipynb 10
_missing_stata_message = (
    "pystata path not found\n"
    "A Stata 17+ installation is required to use the nbstata Stata kernel. "
    "If you already have Stata 17+ installed, "
    "please specify its path in your configuration file."
)

# %% ../nbs/14_kernel.ipynb 12
def _handle_stata_import_error(err, silent, execution_count):
    if not silent:
        print_red(f"ModuleNotFoundError: {_missing_stata_message}")
    return _stata_error_reply(
        ename = "ModuleNotFoundError", 
        evalue = _missing_stata_message, 
        execution_count = execution_count,
    )

# %% ../nbs/14_kernel.ipynb 13
def _handle_stata_init_error(err, silent, execution_count):
    reply_content = _stata_error_reply(
        ename = "Stata init error", 
        evalue = str(err), 
        execution_count = execution_count,
    )
    if not silent:
        print_red(reply_content['evalue'])
    return reply_content

# %% ../nbs/14_kernel.ipynb 17
def print_stata_error(text):
    lines = text.splitlines()
    if len(lines) >= 2 and lines[-2] == lines[-1]:
        lines.pop(-1) # remove duplicate error code glitch in pystata.stata.run multi-line (ex. below)
    if len(lines) > 2:
        print("\n".join(lines[:-2]))
    print_red("\n".join(lines[-2:]))

# %% ../nbs/14_kernel.ipynb 23
def _handle_stata_error(err, silent=False, execution_count=None):
    reply_content = _stata_error_reply(
        ename = "Stata error", 
        evalue = str(err), 
        execution_count = execution_count,
    )
    if not silent:
        print_stata_error(reply_content['evalue'])
    return reply_content

# %% ../nbs/14_kernel.ipynb 24
def _format_user_obj(user_expr_output):
    return dict(
        status='ok',
        data={'text/plain': user_expr_output},
        metadata={},
    )

# %% ../nbs/14_kernel.ipynb 25
def _user_expressions(expressions):
    results = {}
    for key, expr in expressions.items():
        try:
            value = _format_user_obj(user_expression(expr))
        except Exception as err:
            value = _stata_error_reply(
                ename = "Stata user expression error",
                evalue = str(err)
            )
            print_red(value['evalue'])
        results[key] = value
    return results

# %% ../nbs/14_kernel.ipynb 29
@patch_to(PyStataKernel)
def post_do_hook(self):
    self.inspect_output = ""

# %% ../nbs/14_kernel.ipynb 30
@patch_to(PyStataKernel)
def do_execute(self, code, silent,
               store_history=True, user_expressions=None, allow_stdin=False):
    """Execute Stata code cell"""
    if not self.stata_ready:
        try:
            self.init_session() # do this here so config error messages displayed in notebook
        except OSError as err:
            return _handle_stata_init_error(err, silent, self.execution_count)
        except ModuleNotFoundError as err: # this should almost always be preempted by OSErrors now
            return _handle_stata_import_error(err, silent, self.execution_count)
        
    self.shell.execution_count += 1
    code_cell = Cell(self, code, silent)
    try:
        code_cell.run()
    except SystemError as err:
        return _handle_stata_error(err, silent, self.execution_count)
    self.post_do_hook()
    return {
        'status': "ok",
        'execution_count': self.execution_count,
        'payload': [],
        'user_expressions': _user_expressions(user_expressions or {}),
    }

# %% ../nbs/14_kernel.ipynb 32
@patch_to(PyStataKernel)
def do_inspect(self, code, cursor_pos, detail_level=0, omit_sections=()):
    """Display Stata 'describe' output (regardless of cursor position)"""
    if self.stata_ready:
        if not self.inspect_output:
            self.inspect_output = get_inspect(code, cursor_pos, detail_level, omit_sections)
        data = {'text/plain': self.inspect_output}
    else:
        data = {}
    return {"status": "ok", "data": data, "metadata": {}, "found": True}

# %% ../nbs/14_kernel.ipynb 33
@patch_to(PyStataKernel)
def do_complete(self, code, cursor_pos):
    """Provide context-aware tab-autocomplete suggestions"""
    if self.stata_ready:
        cursor_start, cursor_end, matches = self.completions.do(
            code,
            cursor_pos,
        )
    else:
        cursor_start = cursor_end = cursor_pos
        matches = []
    return {
        'status': "ok",
        'cursor_start': cursor_start,
        'cursor_end': cursor_end,
        'metadata': {},
        'matches': matches,
    }

# %% ../nbs/14_kernel.ipynb 34
@patch_to(PyStataKernel)
def do_is_complete(self, code):
    """Overrides IPythonKernel with kernelbase default"""
    return {"status": "unknown"}

# %% ../nbs/14_kernel.ipynb 35
@patch_to(PyStataKernel)
def do_history(
    self,
    hist_access_type,
    output,
    raw,
    session=None,
    start=None,
    stop=None,
    n=None,
    pattern=None,
    unique=False,
):
    """Overrides IPythonKernel with kernelbase default"""
    return {"status": "ok", "history": []}
