# -*- coding: utf-8 -*-
from itertools import product
from copy import deepcopy
from datetime import datetime
from exceller.cell import Cell
from const import CONFIG


def fill_info(report, meta):
    ws = report['Info']

    data = {
        'B6': u'Кастомный отчёт',
        'B14': meta.get('client'),
        'A20': meta.get('category'),
        'B25': get_info_period_date(meta.get('months', [])[-1]),
        'B35': u'нет' if meta.get('filter_client') else u'да',
        'B40': meta.get('cur'),

        'D45': get_info_period_date(meta.get('period_1', {}).get('first_date')),
        'E45': get_info_period_date(meta.get('period_1', {}).get('last_date')),

        'B45': get_info_period_date(meta.get('period_2', {}).get('first_date')),
        'C45': get_info_period_date(meta.get('period_2', {}).get('last_date')),

        'F45': get_info_period_date(min(meta['months'])),
        'H45': get_info_period_date(max(meta['months'])),

        'L1': meta.get('lang', u'Ru'),

        'B30': u'-',
        'B58': u'-',
        'B62': u'-',
        'B66': u'-',
        'B70': u'-'
    }
    for addr, value in data.iteritems():
        ws[addr].text = value

    rng_addr = get_rng_addr(report, None, ws, 14, 7)
    style = ws[rng_addr.split(':')[0]].style
    ws.full_clear_range_2(rng_addr)
    for row, comp in enumerate(meta['competitors'], 14):
        ws.set_cell_value(row, 7, comp)
        ws.set_cell_style(row, 7, style)

    ws = report['Meta']
    ws['P2'].text = meta['segments']['segment_1']
    ws['S2'].text = meta['segments']['segment_2']
    ws['V2'].text = u'Остальные регионы' if meta.get('lang', 'Ru') == 'Ru' else u'Other'

    if meta['def_competitors'] is False:
        for row, comp in enumerate(meta['competitors'], 3):
            ws.set_cell_value(row, 35, comp)
            ws.set_cell_style(row, 35, style)

    return report


def get_info_period_date(date):
    return datetime.strptime(date, '%Y%m').strftime('%Y-%m')


def clear_unused_sheets(report):
    for ws_config in CONFIG['slides_to_clear']:
        for rng_addr in ws_config['ranges']:
            report[ws_config['ws_name']].full_clear_range_2(rng_addr)

    return report


def fill_dyn_slide(report, curs, table_name, slide, meta):
    slide_config = CONFIG.get(slide)

    query_template = slide_config['query_template']
    for i, query_config in enumerate(slide_config['queries']):
        if slide_config.get('worksheets'):
            for ws_config in slide_config['worksheets']:
                query_kwargs = get_query_kwargs(table_name, meta, **query_config.get('query_params', {}))
                query_kwargs.update(ws_config.get('query_params', {}))
                query = {
                    'template': query_template,
                    'cols': slide_config['query_cols'],
                    'kwargs': query_kwargs
                }

                tables = zip_tables(query_config, ws_config, i)
                if slide_config.get('format_ranges'):
                    tables = format_tables(tables, ws_config['params'])

                proceed_table(report, curs, query, tables, ws_config['ws_name'], meta)

        else:
            query = {
                'template': query_template,
                'cols': slide_config['query_cols'],
                'kwargs': get_query_kwargs(table_name, meta, **query_config.get('query_params', {}))
            }
            proceed_table(report, curs, query, query_config['tables'], slide_config['ws_name'], meta)

    return report


def check_competitors(curs, table_name, slide, meta):
    slide_config = CONFIG.get(slide)

    bad_sheets = []
    for ws_config in slide_config['worksheets']:
        query_kwargs = get_query_kwargs(table_name, meta, **ws_config['query_params'])
        curs.execute(slide_config['query_template'].decode('utf-8').format(**query_kwargs))
        data = create_data_from_query(curs, slide_config['query_cols'])

        slices = []
        for key, pretty_name in zip(['Total_Cnt', 'Direct_Cnt', 'RSYA_Cnt'], [u'Суммарно', u'Поиск', u'Сети']):
            if any([val < 3 for val in data[key]]):
                slices.append(pretty_name)

        if slices:
            bad_sheets.append(u'{ws_name} ({column}): {slices}'.format(
                ws_name=ws_config['ws_name'], column=ws_config['query_params']['column'], slices=u', '.join(slices)
            ))

    return bad_sheets


def fill_competitors_table(report, curs, table_name, slide, meta):
    slide_config = CONFIG.get(slide)

    query_template = slide_config['query_template']
    for ws_config, rng_config in product(slide_config['worksheets'], slide_config['ranges']):
        report[ws_config['ws_name']].full_clear_range_2(rng_config['range_to_clear'])

        query_kwargs = get_query_kwargs(table_name, meta, **ws_config['query_params'])
        query_kwargs.update(rng_config['query_params'])

        for col_num, competitor in enumerate(meta['competitors']):
            query_kwargs['competitor'] = competitor
            query = {
                'template': query_template,
                'cols': slide_config['query_cols'],
                'kwargs': query_kwargs
            }
            tables = deepcopy(rng_config['tables'])
            for table in tables:
                table['column'] += col_num

            proceed_table(report, curs, query, tables, ws_config['ws_name'], meta)

    return report


def fill_period_slide(report, curs, table_name, slide, meta):
    slide_config = deepcopy(CONFIG.get(slide))

    query_template = slide_config.get('query_template')
    for i, query_config in enumerate(slide_config['queries']):
        if query_config.get('query_template'):
            query_template = query_config['query_template']

        period = meta.get(query_config['period'], {
            'first_date': '\'None\'',
            'last_date': '\'None\'',
            'name': query_config['period']
        }) if query_config.get('period') else meta['period_2']
        first_date, last_date, period_name = period['first_date'], period['last_date'], period.get('name')

        if slide_config.get('worksheets'):
            for ws_config in slide_config['worksheets']:
                query_kwargs = ws_config.get('query_params', {})
                query_kwargs.update(get_query_kwargs(
                    table_name, meta, first_date=first_date, last_date=last_date, period_name=period_name
                ))
                query_kwargs.update(query_config.get('query_params', {}))

                query = {
                    'template': query_template,
                    'cols': slide_config.get('query_cols', query_config.get('query_cols')),
                    'kwargs': query_kwargs,
                    'params': ws_config.get('params', {})
                }
                if ws_config.get('query_params', {}).get('client_condition'):
                    if isinstance(query['kwargs']['client_condition'], str):
                        query['kwargs']['client_condition'] = query['kwargs']['client_condition'].decode('utf-8')
                    query['kwargs']['client_condition'] = ws_config['query_params']['client_condition'].format(
                        **query['kwargs'])

                tables = zip_tables(query_config, ws_config, i)
                proceed_table(report, curs, query, tables, ws_config['ws_name'], meta)

        else:
            query = {
                'template': query_template,
                'cols': slide_config['query_cols'],
                'kwargs': get_query_kwargs(table_name, meta, first_date=first_date, last_date=last_date,
                                           period_name=period_name)
            }
            proceed_table(report, curs, query, query_config['tables'], slide_config['ws_name'], meta)

    return report


def zip_tables(query_config, ws_config, i):
    tables = []

    tmp_tables = ws_config['queries'][i]['tables'] if ws_config.get('queries') else query_config['tables']
    if not query_config.get('tables'):
        return tmp_tables

    for tab_1, tab_2 in zip(tmp_tables, query_config['tables']):
        if tab_1 and tab_2:
            tab_1.update(tab_2)
        elif tab_2:
            tab_1 = tab_2.copy()

        tables.append(tab_1)

    return tables


def format_tables(table_templates, ws_params):
    def format_ranges(table_ranges):
        ranges = []
        for rng_template in table_ranges:
            ranges.append(rng_template.format(**ws_params))

        return ranges

    tables = []
    for table in table_templates:
        table = deepcopy(table)

        if table.get('period_range'):
            table['period_range'] = table['period_range'].format(**ws_params)

        if table.get('named_ranges'):
            table['named_ranges'] = format_ranges(table['named_ranges'])

        if table.get('update_ranges'):
            table['update_ranges'] = format_ranges(table['update_ranges'])

        if table.get('formulas'):
            for formula in table['formulas']:
                formula['range'] = formula['range'].format(**ws_params)

        tables.append(table)

    return tables


def proceed_table(report, curs, query, tables, ws_name, meta):
    """
    query = {
        'template':
        'cols':
        'kwargs':
        'params': draw_period = True, anonymize = True
    }
    """
    if isinstance(query['template'], str):
        query['template'] = query['template'].decode('utf-8')
    curs.execute(query['template'].format(**query['kwargs']))

    data = create_data_from_query(curs, query['cols'])

    if query.get('params', {}).get('anonymize'):
        clients, comp_num = [], 1
        for client in data['Client']:
            if client != query['kwargs']['client']:
                client = u'Рекламодатель %i' % comp_num
                comp_num += 1
            clients.append(client)
        data['Client'] = clients

    for tab_config in tables:
        if not tab_config.get('query_cols'):
            tab_config['query_cols'] = query['cols']

        if tab_config.get('period_range') or tab_config.get('update_periods'):
            fill_months(report, report[ws_name], tab_config, data['Period'])

        if query.get('params', {}).get('draw_period'):
            report[ws_name].set_cell_value(
                tab_config['row'] - 1, tab_config['column'], query['kwargs']['period_name'])
        fill_table(report, report[ws_name], tab_config, data, meta)

    return report


def create_data_from_query(curs, cols):
    data = {}
    for col_name in cols:
        data[col_name] = []
    for row in curs.fetchall():
        for col_name, val in zip(cols, row):
            data[col_name].append(val if val else 0)
    return data


def get_query_kwargs(table_name, meta, **kwargs):
    query_kwargs = {
        'table_name': table_name,

        'client': meta['client'],
        'is_client_include': 0 if meta['filter_client'] else 1,
        'category': meta['category'],

        'competitors':
            u', '.join([u'"{}"'.format(comp) for comp in meta['competitors']]) if meta['competitors'] else u'\'\'',
        'top_clients':
            u', '.join([u'"{}"'.format(client) for client in meta['top_clients']]) if meta['top_clients']
            else u'\'\'',

        'segment_1': meta['segments']['segment_1'],
        'segment_2': meta['segments']['segment_2'],

        'first_date': meta['period_2']['first_date'],
        'last_date': meta['period_2']['last_date'],
    }
    query_kwargs.update(kwargs)
    return query_kwargs


def fill_months(report, ws, tab_config, months):
    style = ws['A%i' % tab_config['row']].style
    formulas = []
    for column in xrange(2, 5):
        formulas.append(ws.rows[tab_config['row']].cells[column].formula)

    last_cell = get_last_cell(ws, tab_config['row'], 1)
    column = 4 if tab_config.get('update_formulas', True) else 1
    ws.full_clear_range(tab_config['row'], 1, last_cell.row.row_index, column)

    for row, month in enumerate(months, tab_config['row']):
        ws.set_cell_value(row, 1, int(month))
        ws.set_cell_style(row, 1, style)

        if tab_config.get('update_formulas', True):
            for i, formula in enumerate(formulas, 1):
                formula = formula.replace('A%i' % tab_config['row'], 'A%i' % row)
                formula = formula.replace('B%i' % tab_config['row'], 'B%i' % row)
                formula = formula.replace('C%i' % tab_config['row'], 'C%i' % row)

                ws.set_cell_formula(row, 1 + i, formula)

    if tab_config.get('period_range'):
        rng_addr = u'$D${}:$D${}'.format(tab_config['row'], tab_config['row'] + len(months) - 1)
        report.set_named_range(tab_config['period_range'], u'\'{}\'!{}'.format(ws.name, rng_addr))

    return ws


def fill_table(report, ws, tab_config, data, meta):
    for column, (col_name, rng_name) in enumerate(
            zip(tab_config['query_cols'], tab_config.get('named_ranges', data.keys())), tab_config['column']):
        rng_addr = get_rng_addr(report, rng_name, ws, tab_config['row'], column)

        if tab_config.get('clear_range', True):
            style = ws[rng_addr.split(':')[0]].style
            ws.full_clear_range_2(rng_addr)

        row = tab_config['row']
        for row, value in enumerate(data[col_name], tab_config['row']):
            if col_name == 'Client' and value == meta['client']:
                ws.set_cell_formula(row, column, u'Info!$B$14')
            elif col_name == 'Month':
                ws.set_cell_value(row, column, int(value))
            else:
                ws.set_cell_value(row, column, value)
            if tab_config.get('clear_range', True):
                ws.set_cell_style(row, column, style)

        if tab_config.get('named_ranges'):
            rng_addr = u'${}${}:${}${}'.format(
                Cell.column_to_letter(column), tab_config['row'], Cell.column_to_letter(column), row)
            report.set_named_range(rng_name, u'\'{}\'!{}'.format(ws.name, rng_addr))

    if tab_config.get('update_ranges'):
        for rng_name in tab_config['update_ranges']:
            rng_addr = get_rng_addr(report, rng_name, ws)
            cell = ws[rng_addr.split(':')[0]]
            col = Cell.column_to_letter(cell.col_index)

            rng_addr = u'${}${}:${}${}'.format(
                col, tab_config['row'], col, tab_config['row'] + len(data.values()[0]) - 1)
            report.set_named_range(rng_name, u'\'{}\'!{}'.format(ws.name, rng_addr))

    paste_formula(report, ws, tab_config, len(data.values()[0]))

    return ws


def paste_formula(report, ws, tab_config, last_row):
    for formula in tab_config.get('formulas', []):
        style = ws['{}{}'.format(formula['column'], tab_config['row'])].style

        column = Cell.letter_to_column(formula['column'])
        rng_addr = get_rng_addr(report, formula.get('range'), ws, tab_config['row'], column)
        ws.full_clear_range_2(rng_addr)

        row = tab_config['row']
        for row in range(tab_config['row'], tab_config['row'] + last_row):
            try:
                ws.set_cell_formula(row, column, eval(formula['value'], {'row': row}).decode('utf-8'))
            except Exception:
                pass
            ws.set_cell_style(row, column, style)

        if formula.get('range'):
            rng_addr = u'${}${}:${}${}'.format(formula['column'], tab_config['row'], formula['column'], row)
            report.set_named_range(formula['range'], u'\'{}\'!{}'.format(ws.name, rng_addr))

    return ws


def get_rng_addr(report, rng_name, ws, row=None, column=None):
    try:
        rng_addr = report.get_named_range(rng_name).split('!')[1].replace('$', '')
        if len(rng_addr.split(':')) == 2:
            return rng_addr
        else:
            raise Exception

    except Exception:
        first_cell = ws.rows[row].get_cell(column)
        last_cell = get_last_cell(ws, row, column)

        return u'{col_name}{first_row}:{col_name}{last_row}'.format(
            col_name=Cell.column_to_letter(first_cell.col_index),
            first_row=first_cell.row.row_index, last_row=last_cell.row.row_index
        )


def get_last_cell(ws, row, column):
    first_cell = ws.rows[row].get_cell(column)
    last_cell = None
    while ws.rows.get(row):
        if check_cell(ws.rows[row].get_cell(column)):
            last_cell = ws.rows[row].get_cell(column)
            row += 1
        else:
            break

    if last_cell is None:
        last_cell = first_cell

    return last_cell


def check_cell(cell):
    if cell.text or cell.formula:
        return True

    if any([child.get('t') == 'shared' for child in cell._element.getchildren()]):
        return True

    return False
