import csv
from collections import OrderedDict

import requests
import xlrd
import xlwt
import yt.wrapper as yt

from django.core.files.uploadedfile import SimpleUploadedFile


def iterate_xls(file_name, sheet_index=0):
    book = xlrd.open_workbook(file_name)
    sheet = book.sheet_by_index(sheet_index)
    for i in range(sheet.nrows):
        yield sheet.row_values(i)


def iterate_csv(file_name):
    with open(file_name) as f:
        reader = csv.reader(f)
        for row in reader:
            yield row


def download(url, name):
    try:
        response = requests.get(url=url, timeout=10)
        response.raise_for_status()
    except requests.RequestException:
        return None

    return SimpleUploadedFile.from_dict({
        'filename': name,
        'content': response.content,
        'content-type': response.headers['Content-Type'],
    })


class SheetReaderBaseFileAdapter:

    def __init__(self, file_name):
        self.file_name = file_name
        self._iterator = self.iterator()
        self._headers = next(self._iterator)

    def iterator(self):
        raise NotImplementedError

    def __iter__(self):
        return self

    def __next__(self):
        row = [str(col) for col in next(self._iterator)]
        return OrderedDict(zip(self._headers, row))


class SheetReaderXLSAdapter(SheetReaderBaseFileAdapter):

    def iterator(self):
        return iterate_xls(self.file_name)


class SheetReaderCSVAdapter(SheetReaderBaseFileAdapter):

    def iterator(self):
        return iterate_csv(self.file_name)


class SheetReaderYTAdapter:

    def __init__(self, file_name):
        self.file_name = file_name
        self._iterator = yt.read_table(file_name)

    def __iter__(self):
        return self._iterator


class SheetWriterBaseAdapter:

    def __init__(self, file_name):
        self.file_name = file_name
        self._is_headers_set = False

    def writerow(self, data):
        raise NotImplementedError

    def close(self):
        raise NotImplementedError


class SheetWriterDummyAdapter(SheetWriterBaseAdapter):

    def __init__(self):
        super().__init__(None)

    def writerow(self, data):
        pass

    def close(self):
        pass


class SheetWriterBaseFileAdapter(SheetWriterBaseAdapter):

    def writerow(self, data):
        """
        Запись строки в файл.
        Это может быть как список колонок, так и упорядоченный словарь,
        где ключи – это заголовки, значения – колонки
        """
        if isinstance(data, dict):
            self._writerow_as_dict(data)
        else:
            self._writerow(data)

    def _writerow(self, data):
        raise NotImplementedError

    def _writerow_as_dict(self, data):
        if not self._is_headers_set:
            self._writerow(list(data.keys()))
            self._is_headers_set = True
        self._writerow(list(data.values()))

    def _get_proxy(self):
        """
        Используется, чтобы иметь возможность
        выполнять специфичные операции для разных типов табличных файлов.
        """
        raise NotImplementedError

    def __getattr__(self, item):
        return getattr(self._get_proxy(), item)


class SheetWriterXLSAdapter(SheetWriterBaseFileAdapter):

    def __init__(self, file_name):
        super().__init__(file_name)
        self._wb = xlwt.Workbook(encoding='utf8')
        self._sheet = self._wb.add_sheet('sheet')
        self._last_row = 0

    def _writerow(self, data):
        for col, value in enumerate(data):
            self._sheet.write(self._last_row, col, value)
        self._last_row += 1

    def close(self):
        self._wb.save(self.file_name)

    def _get_proxy(self):
        return self._sheet


class SheetWriterCSVAdapter(SheetWriterBaseFileAdapter):

    def __init__(self, file_name):
        super().__init__(file_name)
        self._file = open(file_name, 'w')
        self._writer = csv.writer(self._file)

    def _writerow(self, data):
        self._writer.writerow(data)

    def close(self):
        self._file.close()

    def _get_proxy(self):
        return self._writer


class SheetWriterYTAdapter(SheetWriterBaseAdapter):

    def __init__(self, file_name, chunk_size=None):
        super().__init__(file_name)
        self._data = []
        self._chunk_size = chunk_size
        # Сначала пишем всё во временную таблицу,
        # потом переносим в основную, чтобы все данные появлялись сразу
        self._tmp_table = yt.TablePath(yt.create_temp_table('//home/femida'), append=True)

    def _store(self):
        if self._data:
            yt.write_table(self._tmp_table, self._data)
            self._data.clear()

    def writerow(self, data):
        self._data.append(data)
        if len(self._data) == self._chunk_size:
            self._store()

    def close(self):
        self._store()
        yt.move(self._tmp_table, self.file_name, force=True)
