import csv
from io import BytesIO, StringIO
from os import path

import pyexcel
import pyexcel_xlsx


class Spreadsheet():

    ALLOWED_FILE_EXTENSIONS = ('csv', 'xlsx', 'xls', 'ods', 'xlsm', 'tsv')

    def __init__(self, csv_data=None, rows=None, filename=''):

        self.filename = filename

        if csv_data and rows:
            raise TypeError('Spreadsheet must be created from either rows or CSV data')

        self._csv_data = csv_data or ''
        self._rows = rows or []

    @property
    def as_dict(self):
        return {
            'file_name': self.filename,
            'data': self.as_csv_data
        }

    @property
    def as_csv_data(self):
        if not self._csv_data:
            with StringIO() as converted:
                output = csv.writer(converted)
                for row in self._rows:
                    output.writerow(row)
                self._csv_data = converted.getvalue()
        return self._csv_data

    @classmethod
    def can_handle(cls, filename):
        return cls.get_extension(filename) in cls.ALLOWED_FILE_EXTENSIONS

    @staticmethod
    def get_extension(filename):
        return path.splitext(filename)[1].lower().lstrip('.')

    @staticmethod
    def normalise_newlines(file_content):
        return '\r\n'.join(file_content.read().decode('utf-8').splitlines())

    @classmethod
    def from_rows(cls, rows, filename=''):
        return cls(rows=rows, filename=filename)

    @classmethod
    def from_dict(cls, dictionary, filename=''):
        return cls.from_rows(
            zip(
                *sorted(dictionary.items(), key=lambda pair: pair[0])
            ),
            filename=filename,
        )

    @classmethod
    def from_file(cls, file_content, filename=''):
        extension = cls.get_extension(filename)

        if extension == 'csv':
            return cls(csv_data=Spreadsheet.normalise_newlines(file_content), filename=filename)

        if extension == 'tsv':
            file_content = StringIO(
                Spreadsheet.normalise_newlines(file_content))

        instance = cls.from_rows(
            pyexcel.iget_array(
                file_type=extension,
                file_stream=file_content),
            filename)
        pyexcel.free_resources()
        return instance

    @classmethod
    def from_file_form(cls, form):
        return cls.from_file(
            form.file.data,
            filename=form.file.data.filename,
        )

    @property
    def as_rows(self):
        if not self._rows:
            self._rows = list(csv.reader(
                StringIO(self._csv_data),
                quoting=csv.QUOTE_MINIMAL,
                skipinitialspace=True,
            ))
        return self._rows

    @property
    def as_excel_file(self):
        io = BytesIO()
        pyexcel_xlsx.save_data(io, {'Sheet 1': self.as_rows})
        return io.getvalue()
