import xlsxwriter

from ingest.template.tabs import TabConfig
from .schema_template import SchemaTemplate

DEFAULT_INGEST_URL = "http://api.ingest.data.humancellatlas.org"
DEFAULT_SCHEMAS_ENDPOINT = "/schemas/search/latestSchemas"
DEFAULT_MIGRATIONS_URL = "https://schema.humancellatlas.org/property_migrations"
MIN_WIDTH_OF_COLUMN = 25


class SpreadsheetBuilder():

    def build(self, schema_template):
        """ Builds an empty spreadsheet. """
        raise NotImplementedError("Subclasses of SpreadsheetBuilder are required to override this method.")

    def create_initial_spreadsheet(self, output_file, hide_row):
        """
        Creates an initial empty spreadsheet with a set formatting.

        :param output_file: A string representing the name of the Excel file that will be generated by this class.
        :param hide_row: A boolean where when true, hides the third row in the generated spreadsheet which contains
                         the fully qualified name of the metadata schema field.
        """

        self.spreadsheet = xlsxwriter.Workbook(output_file)
        self.header_format = self.spreadsheet.add_format(
            {'bold': True, 'bg_color': '#D0D0D0', 'font_size': 12, 'valign': 'vcenter'})
        self.locked_format = self.spreadsheet.add_format({'locked': True})
        self.desc_format = self.spreadsheet.add_format(
            {'font_color': '#808080', 'italic': True, 'text_wrap': True, 'font_size': 12, 'valign': 'top'})
        self.include_schemas_tab = False
        self.hidden_row = hide_row

    def save_spreadsheet(self):
        self.spreadsheet.close()

    def generate_spreadsheet(self, schema_urls=None, tabs_template=None, include_schemas_tab=False):
        """
        Given a template that represents the tabs configuration in the desired spreadsheet and a metadata schema,
        generates the respective spreadsheet. If include_schema_tab is set to True, the spreadsheet will have an
        additional sheet containing the URLs of the metadata schemas that were used to generate the spreadsheet.

        :param schema_urls: A list of strings where each string represents a URL containing a JSON-formatted metadata
                            schema.
        :param tabs_template: A string representing a YAML file that contains a configuration specifying how the tabs in
                              the generated spreadsheet should look and what information/columns it should contain.
        :param include_schemas_tab: A boolean, when true, includes an additional sheet at the end of the spreadsheet
                                    that contains a list of metadata schemas that were used to create the spreadsheet.
        """

        self.include_schemas_tab = include_schemas_tab
        if tabs_template and schema_urls:
            tabs_parser = TabConfig()
            tabs = tabs_parser.load(tabs_template)
            template = SchemaTemplate(metadata_schema_urls=schema_urls, tab_config=tabs)
        elif schema_urls:
            template = SchemaTemplate(metadata_schema_urls=schema_urls)
        else:
            template = SchemaTemplate()

        self.build(template)

    def get_value_for_column(self, template, col_name, property):
        """ Lookup the value of a column name and property in a SchemaTemplate. `lookup` throws an exception if no
        property exists so capture the exception and return an empty string if no property exists for the given column
        and template."""
        # TODO(maniarathi): What is a template? The metadata schema JSON?
        try:
            return str(template.lookup(col_name + "." + property)) if template.lookup(col_name + "." + property) else ""
        except Exception:
            print("No property " + property + " for " + col_name)
            return ""

    def get_user_friendly_column_name(self, template, column_name, primary_schema=None):
        # TODO(maniarathi): Make this description better.
        """ Given a column name derived originally from a metadata schema file that will be inputted as a column name
        into the generated spreadsheet, turn it into a user friendly name. """
        if '.text' in column_name:
            key = column_name.replace('.text', '') + ".user_friendly"
        elif '.ontology_label' in column_name:
            key = column_name.replace('.ontology_label', '') + ".user_friendly"
        elif '.ontology' in column_name:
            key = column_name.replace('.ontology', '') + ".user_friendly"
        else:
            key = column_name + ".user_friendly"

        try:
            uf = str(template.lookup(key)) if template.lookup(key) else column_name

            # **TO DO:** If customisable user friendly names are ever implemented in the metadata schemas,
            # remove the customisation code below
            # For any purchased_reagents or barcode module imports that aren't arrays, prepend each user friendly
            # name with the name of the wrapper property
            wrapper = ".".join(column_name.split(".")[:-1])
            if template.lookup(wrapper)['schema']['module'] \
                    and (template.lookup(wrapper)['schema']['module'] == 'purchased_reagents' or
                         template.lookup(wrapper)['schema']['module'] == 'barcode') and not \
                    template.lookup(wrapper)['multivalue']:
                uf = template.lookup(wrapper)['user_friendly'] + " - " + uf
            if '.ontology_label' in column_name:
                uf = uf + " ontology label"
            if '.ontology' in column_name:
                uf = uf + " ontology ID"

            # For core fields such as Biomaterial ID or Protocol name, update the user-friendly name to the more
            # specific type, eg
            # Donor organism ID or Library preparation protocol name
            # For linking biomaterial fields, also prepend the field with "Input"
            if "Biomaterial " in uf:
                schema_name = column_name.split(".")[0]

                for schema in template.tab_config().lookup('tabs'):
                    if schema_name == list(schema.keys())[0]:
                        schema_uf = schema[schema_name]['display_name']
                uf = uf.replace("Biomaterial", schema_uf)

                if primary_schema != schema_name:
                    uf = "Input " + uf

            if "Protocol " in uf:
                schema_name = column_name.split(".")[0]

                for schema in template.tab_config().lookup('tabs'):
                    if schema_name == list(schema.keys())[0]:
                        schema_uf = schema[schema_name]['display_name']
                uf = uf.replace("Protocol", schema_uf)

            return uf
        except Exception:
            return key

    def generate_and_add_schema_worksheet_to_spreadsheet(self, schema_urls):
        worksheet = self.spreadsheet.add_worksheet("Schemas")
        worksheet.write(0, 0, "Schemas")
        for index, url in enumerate(schema_urls):
            worksheet.write(index + 1, 0, url)
