"""This module holds the xls utils of the ``common`` app"""

import os
import re
import string
import unicodedata

from openpyxl import Workbook
from openpyxl.cell import get_column_letter
from openpyxl.drawing import Image
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side, Style

from django.conf import settings

DEFAULT_LOGO = os.path.join(settings.STATIC_ROOT, 'trainings/images/logo.png')

COLUMN_MAX_WIDTH = 40

TOP_OFFSET = 2
RIGHT_OFFSET = 50
BOTTOM_OFFSET = 50
LEFT_OFFSET = 1

PRIMARY_COLOR = '8F50509E'
SECONDARY_COLOR = '8F3E3E92'
TERTIARY_COLOR = '8F8FC9A2'
QUATERNARY_COLOR = '8F474363'
QUINARY_COLOR = '8F474363'
SENARY_COLOR = '8F302F40'
DEFAULT_COLOR = 'FFFFFFFF'
EMPTY_COLOR = 'FFFFFFFF'

BLACK = 'FF000000'
WHITE = 'FFFFFFFF'

MEDIUM = 'medium'
SOLID = 'solid'
HAIR = 'hair'


def remove_accents(data):
    return ''.join(x for x in unicodedata.normalize(
        'NFKD', data) if x in string.ascii_letters).lower()


class XLSWriter:

    STYLES = {
        'NAMES_HEADER_STYLE': Style(
            fill=PatternFill(fill_type=SOLID, start_color=SECONDARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'NAMES_STYLE': Style(
            fill=PatternFill(fill_type=SOLID, start_color=QUATERNARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=HAIR, color=BLACK),
                bottom=Side(border_style=HAIR, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'NAMES_STYLE_LAST': Style(
            fill=PatternFill(fill_type=SOLID, start_color=QUATERNARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=HAIR, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'GOOD_ANSWER_STYLE': Style(
            fill=PatternFill(fill_type=SOLID, start_color=QUINARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True, horizontal='center')
        ),
        'PRIMARY_HEADER': Style(
            fill=PatternFill(fill_type=SOLID, start_color=PRIMARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'SECONDARY_HEADER': Style(
            fill=PatternFill(fill_type=SOLID, start_color=SECONDARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'TERTIARY_HEADER': Style(
            fill=PatternFill(fill_type=SOLID, start_color=TERTIARY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
            ),
            font=Font(color=WHITE),
            alignment=Alignment(wrap_text=True)
        ),
        'DEFAULT': Style(
            fill=PatternFill(fill_type=SOLID, start_color=DEFAULT_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=HAIR, color=BLACK),
                bottom=Side(border_style=HAIR, color=BLACK),
            ),
            font=Font(color=BLACK),
            alignment=Alignment(wrap_text=True)
        ),
        'DEFAULT_LAST': Style(
            fill=PatternFill(fill_type=SOLID, start_color=DEFAULT_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=HAIR, color=BLACK),
            ),
            font=Font(color=BLACK),
            alignment=Alignment(wrap_text=True)
        ),
        'EMPTY': Style(
            fill=PatternFill(fill_type=SOLID, start_color=EMPTY_COLOR),
        ),
        'HEADER_INFO': Style(
            font=Font(size=14, bold=True),
            fill=PatternFill(fill_type=SOLID, start_color=EMPTY_COLOR),
            alignment=Alignment(wrap_text=True, horizontal='right')
        ),
        'SHEET_NAME': Style(
            font=Font(size=14, bold=True, italic=True, color='FF888888'),
            fill=PatternFill(fill_type=SOLID, start_color=EMPTY_COLOR),
        ),
        'ABSTRACT_TITLE': Style(
            font=Font(size=20, bold=True),
            fill=PatternFill(fill_type=SOLID, start_color=EMPTY_COLOR),
            border=Border(
                left=Side(border_style=MEDIUM, color=BLACK),
                top=Side(border_style=MEDIUM, color=BLACK),
                bottom=Side(border_style=MEDIUM, color=BLACK),
                right=Side(border_style=MEDIUM, color=BLACK),
            ),
            alignment=Alignment(wrap_text=True, horizontal='center')
        ),
        'ABSTRACT_SUB_TITLE': Style(
            font=Font(size=11, bold=True, underline='single'),
            fill=PatternFill(fill_type=SOLID, start_color=EMPTY_COLOR),
        )
    }

    def __init__(self, file, logo=DEFAULT_LOGO,
                 header_info='Magency Training Plateform', doc_title='Export'):
        self.file = file
        self.wb = Workbook()
        self.names = {}
        self.nrows = {}
        self.max_ncolumns = {}
        self.widths = {}
        self.empty_rows = {}
        self.logo = logo
        self.header_info = header_info
        self.doc_title = doc_title

    def add_sheet(self, name):
        sheet_name_short = name[0:28]
        sheet_name_short = re.sub('[^-a-zA-Z0-9]', '', sheet_name_short)
        sheet_name_short = '%d-%s' % (len(self.wb.worksheets), sheet_name_short)

        ws = self.wb.create_sheet()
        ws.title = sheet_name_short
        self.names[sheet_name_short] = name
        self.widths[sheet_name_short] = {}
        self.empty_rows[sheet_name_short] = {}
        self.nrows[sheet_name_short] = 1 + TOP_OFFSET
        self.max_ncolumns[sheet_name_short] = 1 + LEFT_OFFSET
        return sheet_name_short

    def update_row_width(self, column, sheet_name, new_elem):
        if not isinstance(new_elem, str):
            new_elem = str(new_elem)
        width = len(new_elem)
        if width > COLUMN_MAX_WIDTH:
            width = COLUMN_MAX_WIDTH
        width = int(width * 1.20)
        if width > self.widths[sheet_name].get(column, 0):
            self.widths[sheet_name][column] = width
            self.wb[sheet_name].column_dimensions[get_column_letter(column)].width = width

    def add_empty_row(self, sheet_name, style_name='EMPTY'):
        brow = self.nrows[sheet_name]
        self.empty_rows[sheet_name][brow] = style_name
        self.nrows[sheet_name] = self.nrows[sheet_name] + 1

    def writerow(self, row, sheet_name, style_name='EMPTY'):
        ws = self.wb[sheet_name]
        brow = self.nrows[sheet_name]
        for index, elem in enumerate(row):
            cell = ws.cell(row=brow, column=index + LEFT_OFFSET + 1)
            if isinstance(elem, (list, tuple)):
                elem, style_name = elem

            # So, we might have an encoding exception when putting
            # the value in the cell, but we don't really know
            # what kind of exception this might raises...
            try:
                cell.value = elem
            except Exception:
                cell.value = ''
            if style_name:
                if style_name not in self.STYLES:
                    style_name = 'DEFAULT'
            else:
                style_name = 'EMPTY'
            cell.style = self.STYLES[style_name]
            self.update_row_width(index + LEFT_OFFSET + 1, sheet_name, elem)
            if index + LEFT_OFFSET + 1 > self.max_ncolumns[sheet_name]:
                self.max_ncolumns[sheet_name] = index + LEFT_OFFSET + 1
        self.nrows[sheet_name] = self.nrows[sheet_name] + 1

    def writerows(self, rows, sheet_name, style=None):
        for row in rows:
            self.writerow(row, sheet_name, style)

    def save(self):
        for index, sheet in enumerate(self.wb):
            if index == 0:
                continue
            x_max = self.max_ncolumns[sheet.title]
            y_max = self.nrows[sheet.title]

            # Add border to last row
            for i in range(LEFT_OFFSET + 1, x_max + 1):
                cell = sheet.cell(row=y_max - 1, column=i)
                if cell.style == self.STYLES['DEFAULT']:
                    cell.style = self.STYLES['DEFAULT_LAST']

                if cell.style == self.STYLES['NAMES_STYLE']:
                    cell.style = self.STYLES['NAMES_STYLE_LAST']

            # Add row and column offset
            for i in range(1, x_max + RIGHT_OFFSET + 1):
                for j in range(1, TOP_OFFSET + 1):
                    cell = sheet.cell(row=j, column=i)
                    cell.style = self.STYLES['EMPTY']
            for i in range(1, LEFT_OFFSET + 1):
                for j in range(TOP_OFFSET + 1, y_max + BOTTOM_OFFSET):
                    cell = sheet.cell(row=j, column=i)
                    cell.style = self.STYLES['EMPTY']
            for i in range(x_max + 1, x_max + RIGHT_OFFSET + 1):
                for j in range(TOP_OFFSET + 1, y_max + BOTTOM_OFFSET):
                    cell = sheet.cell(row=j, column=i)
                    cell.style = self.STYLES['EMPTY']
            for i in range(LEFT_OFFSET + 1, x_max + 1):
                for j in range(y_max, y_max + BOTTOM_OFFSET):
                    cell = sheet.cell(row=j, column=i)
                    cell.style = self.STYLES['EMPTY']

            # Add sheet tile
            cell = sheet.cell(row=1, column=1)
            cell.value = self.names[sheet.title]
            cell.style = self.STYLES['SHEET_NAME']

            # Add the empty rows
            for brow in self.empty_rows[sheet.title]:
                for i in range(1, x_max + RIGHT_OFFSET + 1):
                    cell = sheet.cell(row=brow, column=i)
                    cell.style = self.STYLES[self.empty_rows[sheet.title][brow]]

        self.add_abstract()

        self.wb.save(self.file)

    def add_abstract(self):

        aws = self.wb.worksheets[0]

        # change sheet name
        aws.title = 'Abstract'

        # clean styles
        for i in range(1, 100):
            for j in range(1, 100):
                cell = aws.cell(row=j, column=i)
                cell.style = self.STYLES['EMPTY']

        # add logo
        logo = Image(self.logo)
        logo.anchor(aws['B2'], anchortype='oneCell')
        aws.add_image(logo)

        # add header infos
        aws['B2'].style = self.STYLES['HEADER_INFO']
        aws['B2'].value = self.header_info

        # add titles
        aws['B9'].style = self.STYLES['ABSTRACT_TITLE']
        aws['B9'].value = self.doc_title

        # add abstract
        aws['B13'].style = self.STYLES['ABSTRACT_SUB_TITLE']
        aws['B13'].value = 'Sheets in this file'

        for index, ws in enumerate(self.wb):
            if index == 0:
                continue
            cell = aws.cell(row=index + 15, column=2)
            cell.value = ws.title

        # set column dimensions
        aws.column_dimensions[get_column_letter(1)].width = 5
        aws.column_dimensions[get_column_letter(2)].width = 150
