import click
import emoji
import json
import os

from jupytoc.util import EMOJI_SUCESS, EMOJI_WARNING, EMOJI_FAILURE, EMOJI_JUPYTOC
from jupytoc.util import find_notebooks_recursively, get_tuple_without_item, is_file_not_empty


class Jupytoc(object):

    def __init__(self, path, level, title, stdout):
        self.notebook_path = path
        self.credits = '<!-- Table of Contents generated by Jupytoc -->'
        self.maxlevel = level
        self.title = title if title != '' else '**Table of Contents**'
        self.stdout = stdout
        self.compute_toc()

    def read_notebook_content(self):
        with open(self.notebook_path, 'r') as file:
            return json.load(file)

    def find_existing_toc(self, ipynb):
        for i, cell in enumerate(ipynb['cells']):
            if cell['cell_type'] == 'markdown' and self.credits + '\n' in cell['source']:
                return (True, i)
        return (False, None)

    def get_markdown_cells(self, ipynb):
        return [cell for cell in ipynb['cells'] if cell['cell_type'] == 'markdown']

    def get_markdown_lines(self, markdown_cells):
        return [[line for line in cell['source']] for cell in markdown_cells]

    def get_headers(self, markdown_lines):
        for lines in markdown_lines:
            for line in lines:
                stripped_right = line.rstrip('#')
                stripped_both = stripped_right.lstrip('#')
                stripped_wspace = stripped_both.strip()
                level = len(stripped_right) - len(stripped_both)
                link_anchor = lambda header: '#{}'.format(header.replace(' ', '-'))
                if level > 0 and level <= self.maxlevel:
                    yield (level, stripped_wspace, link_anchor(stripped_wspace))

    def build_toc(self, headers):
        return [self.credits + '\n'] + [self.title + '\n'] + ['{nb_spaces}- [{title}]({link})\n'.format(
            nb_spaces=' ' * 2 * (level - 1),
            title=title,
            link=link_anchor
        ) for level, title, link_anchor in headers]

    def write_toc(self, ipynb, toc, update_toc, position):
        markdown_cell = lambda toc: {
            'cell_type': 'markdown',
            'metadata': {},
            'source': toc
        }

        if update_toc:
            click.secho('\t{}  TOC updated.\t[{}]'.format(
                EMOJI_SUCESS, self.notebook_path), fg='green')
            ipynb['cells'][position] = markdown_cell(toc)
        else:
            ipynb['cells'].insert(0, markdown_cell(toc))
            click.secho('\t{}  TOC added.\t[{}]'.format(
                EMOJI_SUCESS, self.notebook_path), fg='green')

        with open(self.notebook_path, 'w') as file:
            file.write(json.dumps(ipynb, indent=2))

    def compute_toc(self):
        try:
            ipynb = self.read_notebook_content()
            markdown_cells = self.get_markdown_cells(ipynb)
            update_toc, position = self.find_existing_toc(ipynb)
            markdown_lines = self.get_markdown_lines(markdown_cells)
            headers = self.get_headers(markdown_lines)
            toc = self.build_toc(headers)
            if not self.stdout:
                self.write_toc(ipynb, toc, update_toc, position)
            else:
                click.secho('\t{}  TOC built.\t[{}]'.format(
                    EMOJI_SUCESS, self.notebook_path), fg='green')
                click.echo(''.join(toc))
        except Exception as err:
            click.secho(
                '\t{}  Build failed.\t[{}]'.format(
                    EMOJI_FAILURE, self.notebook_path),
                fg='red')
            raise(err)


@click.command()
@click.argument('notebooks', nargs=-1, type=click.Path(exists=True))
@click.option('-R', '--recursive', default=False, is_flag=True, help='build TOC for all subdirectories recursively')
@click.option('-l', '--maxlevel', default=6, help='limit TOC entries to headings only up to the specified level')
@click.option('-t', '--title', default='', help='custom TOC title')
@click.option('-s', '--stdout', default=False, is_flag=True, help='print to stdout')
def cli(notebooks, recursive, maxlevel, title, stdout):
    # If we give a path to a folder, we apply a recursive search
    # of all .ipynb files existing in it
    for notebook in notebooks:
        if os.path.isdir(notebook):
            notebooks += tuple(find_notebooks_recursively(root_path=notebook))
            notebooks = get_tuple_without_item(notebooks, notebook)

    if recursive:
        notebooks += tuple(find_notebooks_recursively())

    click.secho('{}  Jupytoc is building TOC for {} notebooks'.format(
        EMOJI_JUPYTOC, len(notebooks)), bold=True)

    for notebook in notebooks:
        if is_file_not_empty(notebook):
            Jupytoc(path=notebook, level=maxlevel, title=title, stdout=stdout)
        else:
            click.secho(
                '\t{}  Empty file.\t[{}]'.format(
                    EMOJI_WARNING, notebook),
                fg='yellow')

    click.echo('Done.')

if __name__ == '__main__':
    cli()
