from datetime import timedelta


def _intervals(start, end, step):
    while start + step < end:
        yield (start, start + step - timedelta(seconds=1))
        start += step

    yield (start, end)


def _batch(lst, batch_size):
    length = len(lst)
    for lst_ix in range(0, length, batch_size):
        yield lst[lst_ix:min(lst_ix + batch_size, length)]


def _download(report, scale, download_tree_max_distance, date_min, date_max):
    print('downloading... ', end='', flush=True)

    # For more information about Stat API parameters, see:
    # https://wiki.yandex-team.ru/statbox/Statface/externalreports/#vygruzkadannyx
    result = report.download_data(
        scale=scale,
        date_min=date_min,
        date_max=date_max,
        _allow_default_dimensions=0,
        _fill_missing_dates=0,
        _enable_calculations=0,
        _apply_dictionary=0,
        _cut_tree_dict=0,
        max_distance=download_tree_max_distance,
    )

    if len(result) == 0:
        print('no data', end='', flush=True)
    else:
        print(f'{len(result)} rows', end='', flush=True)

    return result


def _upload(data, report, scale, batch_size):
    total_rows = len(data)
    print(f'uploading {total_rows} rows... ', end='', flush=True)

    uploaded_rows = 0
    for data_to_be_uploaded in _batch(data, batch_size):
        report.upload_data(scale=scale, data=data_to_be_uploaded)
        uploaded_rows += len(data_to_be_uploaded)
        print(f'{uploaded_rows / total_rows * 100:.0F}%... ', end='', flush=True)

    print('OK', flush=True)


def _add_new_tree_dimension(data, name, default_value):
    result = list()

    tree = default_value.split('\t')[1:-1]   # [1:-1] removes empty values from the begin and end of the list
    for item in data:
        for level in range(1, len(tree) + 1):
            level_value = '\t' + '\t'.join(tree[0:level]) + '\t'
            new_item = item.copy()
            new_item[name] = level_value
            result.append(new_item)

    return result


def _add_new_tree_dimensions(data, new_tree_dimensions):
    if len(new_tree_dimensions) == 0:
        return data

    result = data.copy()
    for name, default_value in new_tree_dimensions.items():
        result = _add_new_tree_dimension(result, name, default_value)

    return result


def copy_report(src, dst, download_batch, upload_batch, min_date, max_date, scale, download_tree_max_distance,
                new_dimensions, new_tree_dimensions, rename_dimensions):
    for start_date, end_date in _intervals(min_date, max_date, timedelta(days=download_batch)):
        print(f'Copy data in interval [{start_date}; {end_date}]: ', end='')

        data = _download(src, scale, download_tree_max_distance, start_date, end_date)
        if len(data) == 0:
            print(flush=True)
            continue

        if len(rename_dimensions):
            for item in data:
                for old_name, new_name in rename_dimensions.items():
                    item[new_name] = item.pop(old_name)

        if len(new_dimensions):
            for item in data:
                item.update(new_dimensions)

        data = _add_new_tree_dimensions(data, new_tree_dimensions)

        print(', ', end='', flush=True)
        _upload(data, dst, scale, upload_batch)
