import logging
from datetime import datetime

from ..data.sector import Sector
from ..standard.codelist import CodelistItem, CodelistSet
from ..utils.abstract import GenericType


class StringType(GenericType):
    def where(self, operation, value):
        if operation in ["contains", "startswith"]:
            if operation == "startswith":
                operation = "starts-with"
            return '{expr}[{operation}(., "{value}")]'.format(
                expr=self.get(),
                operation=operation,
                value=value,
            )
        return super(StringType, self).where(operation, value)


class DateType(GenericType):
    def where(self, operation, value):
        operator = {
            "lt": "<",
            "lte": "<=",
            "gt": ">",
            "gte": ">=",
            "eq": "=",
        }.get(operation)
        if operator:
            tmpl = 'number(translate({expr}, "-", "")) {operator} {value}'
            return tmpl.format(
                expr=self.get(),
                operator=operator,
                value=str(value).replace("-", ""),
            )
        return super(DateType, self).where(operation, value)

    def run(self, etree):
        dates = []
        dates_str = etree.xpath(self.get())
        for date_str in dates_str:
            try:
                dates.append(datetime.strptime(date_str, "%Y-%m-%d").date())
            except ValueError:
                logging.getLogger(__name__).warning('Invalid date: "%s"', date_str)
        return dates


class SectorType(GenericType):
    def __init__(self, expr, condition):
        super(SectorType, self).__init__(expr)
        self.condition = condition

    @staticmethod
    def _vocab_condition(conditions):
        conditions_list = []
        if not isinstance(conditions, list):
            conditions = [conditions]
        for condition in conditions:
            if condition is None:
                conditions_list.append("not(@vocabulary)")
            else:
                conditions_list.append('@vocabulary = "{}"'.format(condition))
        conditions_str = " or ".join(conditions_list)
        if len(conditions_list) > 1:
            conditions_str = "({})".format(conditions_str)
        return conditions_str

    def where(self, operation, value):
        if operation == "in":
            if not isinstance(value, Sector) or value.vocabulary.code != "2":
                raise Exception("{} is not a sector category".format(value))
            codelist_items = (
                CodelistSet().get("Sector").where(category=value.code.code).all()
            )
            conditions = " or ".join(
                ['@code = "{code}"'.format(code=c.code) for c in codelist_items]
            )
            conditions = ["(" + conditions + ")"]
            conditions.append(self._vocab_condition(self.condition.get("1")))
            return "{expr}[{conditions}]".format(
                expr=self.get(),
                conditions=" and ".join(conditions),
            )
        elif operation == "eq":
            conditions = []
            if not isinstance(value, Sector):
                raise Exception("{} is not a sector".format(value))
            if isinstance(value.code, CodelistItem):
                code = value.code.code
            else:
                code = value.code
            if code is not None:
                conditions.append('@code = "{code}"'.format(code=code))
            if value.vocabulary is not None:
                conds = self.condition.get(value.vocabulary.code, value.vocabulary.code)
                conditions.append(self._vocab_condition(conds))
            return "{expr}[{conditions}]".format(
                expr=self.get(),
                conditions=" and ".join(conditions),
            )
        return super(SectorType, self).where(operation, value)

    def run(self, etree):
        return [
            Sector(
                x.get("code"),
                vocabulary=x.get("vocabulary", "1"),
                percentage=x.get("percentage"),
            )
            for x in etree.xpath(self.get())
        ]


class XPathType(GenericType):
    def where(self, operation, value):
        return value


class BooleanType(GenericType):
    def run(self, etree):
        return etree.xpath(
            '{expr} = "true" or {expr} = "1"'.format(
                expr=self.get(),
            )
        )

    def where(self, operation, value):
        if value is not bool(value):
            raise Exception("{} is not a boolean".format(value))
        if value:
            return '{expr} = "true" or {expr} = "1"'.format(
                expr=self.get(),
            )
        else:
            return 'not({expr}) or {expr} = "false" or {expr} = "0"'.format(
                expr=self.get(),
            )
