import math
import os

import xmltodict
from patent_client import CACHE_CONFIG
from patent_client import SETTINGS
from patent_client.util import Manager
from patent_client.util.manager import resolve

from .schema import InpadocBiblioSchema
from .schema import InpadocResultSchema
from .session import NS
from .session import session

SEARCH_FIELDS = {
    "title": "title",
    "abstract": "abstract",
    "title_and_abstract": "titleandabstract",
    "inventor": "inventor",
    "applicant": "applicant",
    "inventor_or_applicant": "inventorandapplicant",
    "publication": "publicationnumber",
    "epodoc_publication": "spn",
    "application": "applicationnumber",
    "epodoc_application": "sap",
    "priority": "prioritynumber",
    "epodoc_priority": "spr",
    "number": "num",  # Pub, App, or Priority Number
    "publication_date": "publicationdate",  # yyyy, yyyyMM, yyyyMMdd, yyyy-MM, yyyy-MM-dd
    "citation": "citation",
    "cited_in_examination": "ex",
    "cited_in_opposition": "op",
    "cited_by_applicant": "rf",
    "other_citation": "oc",
    "family": "famn",
    "cpc_class": "cpc",
    "ipc_class": "ipc",
    "ipc_core_invention_class": "ci",
    "ipc_core_additional_class": "cn",
    "ipc_advanced_class": "ai",
    "ipc_advanced_additional_class": "an",
    "ipc_core_class": "c",
    "classification": "cl",  # IPC or CPC Class
    "full_text": "txt",  # title, abstract, inventor and applicant
}


class OPSException(Exception):
    pass


class InpadocManager(Manager):
    page_size = 20
    primary_key = "publication"
    constituent = None

    def __init__(self, *args, **kwargs):
        super(InpadocManager, self).__init__(*args, **kwargs)
        self.pages = dict()

    def __len__(self):
        page = self.get_page(0)
        max_length = int(resolve(page, "@total-result-count"))
        limit = self.config["limit"]
        if limit:
            return limit if limit < max_length else max_length
        else:
            return max_length

    def _get_results(self):
        offset = self.config["offset"]
        limit = self.config["limit"]

        def result_gen(offset, limit):
            num_pages = math.ceil(len(self) / self.page_size)
            page_num = int(offset / self.page_size)
            counter = page_num * self.page_size
            while page_num < num_pages:
                page_data = self.get_page(page_num)
                data = resolve(page_data, self.item_path)
                if not isinstance(data, list):
                    yield counter, data
                    counter += 1
                else:
                    for item in data:
                        if not self.config["limit"] or counter < self.config["limit"]:
                            yield counter, item
                        counter += 1
                page_num += 1

        # Implement limit and offset
        for counter, item in result_gen(offset, limit):
            if limit is not None and counter - offset > limit:
                break
            if counter >= offset:
                yield self.schema.load(item)

    @property
    def search_url(self):
        if self.constituent:
            return f"http://ops.epo.org/3.2/rest-services/published-data/search/{self.constituent}"
        return "http://ops.epo.org/3.2/rest-services/published-data/search"

    @property
    def item_path(self):
        if self.constituent:
            return "search-result.exchange-documents"
        else:
            return "search-result.publication-reference"

    @property
    def schema(self):
        if self.constituent == "biblio":
            return InpadocBiblioSchema()
        elif self.constituent is None:
            return InpadocResultSchema()

    def get_page(self, page_number):
        if page_number not in self.pages:
            query_params = self.query_params(page_number)
            response = session.get(self.search_url, params=query_params, timeout=10)
            data = xmltodict.parse(
                response.text, process_namespaces=True, namespaces=NS
            )
            self.pages[page_number] = resolve(data, "world-patent-data.biblio-search")
        return self.pages[page_number]

    def query_params(self, page_number):
        query_dict = self.config["filter"]
        start = page_number * self.page_size + 1
        range_q = f"{start}-{start + self.page_size}"
        if "cql_query" in query_dict:
            return dict(q=query_dict["cql_query"], Range=range_q)
        query = ""
        for keyword, values in query_dict.items():
            for value in values:
                if keyword:
                    query += f'{SEARCH_FIELDS[keyword]}="{value}",'
        query = dict(q=query, Range=range_q)
        return query


class InpadocBiblioManager(InpadocManager):
    constituent = "biblio"
