import re
import logging
import threading
import time
from flask import Flask, jsonify, request
import netrc
import json
from json.decoder import JSONDecodeError
from platform import uname
from sys import platform
from urllib.request import urlopen
from habanero import Crossref
from waitress import serve

if platform in ("linux", "linux2"):
    SEPARATOR_FILE_PATH = "/"
    if "microsoft-standard" in uname().release:
        NETRC_FILE = "/home/mtuszowski/.netrc"
    else:
        NETRC_FILE = "/home/pi/.netrc"
elif platform == "win32":
    NETRC_FILE = "C:\\Users\\mtusz\\.netrc"

#HOST_ADDRESS = "192.168.1.191"
HOST_ADDRESS = "0.0.0.0"
PORT_ADDRESS = 5001

class Librarian(object):

    def __init__(self) -> None:
        netrc_mod = netrc.netrc(NETRC_FILE)
        authTokens = netrc_mod.authenticators("crossref")
        self.cr = Crossref(mailto=authTokens[0])
        self.limit = 20

    def search_crossref(self, query, uuid):
        search_result_from_cr = self.cr.works(query = query, limit = self.limit)
        #add to sql with UUID
        #sql row - title, doi, author, uuid, last retrieved, is_available
        with open("results.json", "r+", encoding="UTF-8") as data_file:
            # First we load existing data into a dict.
            try:
                file_data = json.load(data_file)
            except JSONDecodeError:
                file_data = []
            # Join new_data with file_data inside emp_details
            summarized_results = []
            for item in search_result_from_cr['message']['items']:
                container = "NO"
                if "container-title" in item:
                    container = "YES"
                print(item['title'])
                print(item['DOI'])
                summarized_results.append({"DOI" : item['DOI'], "title": item['title'], "type": item['type'], "container": container})
            result = {"UUID": uuid, "total_results": search_result_from_cr['message']['total-results'], "on_page" : 1, "summary":summarized_results, "results":search_result_from_cr['message']['items'],  }
            file_data.append(result)
            print("Saving")
            data_file.seek(0)
            # convert back to json.
            json.dump(file_data, data_file, indent=4)
            return result


    def check_if_exists(self,page_url):
        in_scihub_db = True
        if not page_url.startswith(("http:", "https:")):
            raise ValueError("URL must start with 'http:' or 'https:'")
        # trunk-ignore(bandit/B310)
        with urlopen(page_url) as response:
            info = response.info()
            data = response.read()
            text = data.decode("utf-8")
            for line in text.splitlines():
                if re.match(r".*Unfortunately, Sci-Hub doesn't have the requested document.*", line):
                    return None
        if in_scihub_db:
            return text
        return None

    def download(self, page_url):
        if response := self.check_if_exists(page_url):
            for line in response.splitlines():
                if m := re.match(r".*<embed type=\"application.pdf\"\s*src=\"(.*\.pdf)",line):
                    return "https:" + str(m.group(1))
        return None

    def provide_sci_hub_link(self, page_url):
        if self.check_if_exists(page_url):
            return page_url
        return None

    def refine_query(self, unrefined_reqult):
        print("Refine result")
        refined_result = []
        for item in unrefined_reqult["summary"]:
            if item["container"] == "NO":
                item_link = "https://sci-hub.se/" + str(item['DOI'])
                if self.check_if_exists(item_link):
                    refined_result.append(item)
                    print (item_link)
                time.sleep(0.2)
        return refined_result

    def answer_query(self, query, uuid):
        #Search sql for UUID
        #SQL SELECT * FROM RESULTS WHERE UUID = uuid
        print("Answer result")
        uuid_found = False
        database = None
        with open("results.json", "r", encoding="UTF-8") as new_file:
            #tu logika komunikacji z SQL i cała logika związana z wyszukaniem
            #ale tymczasowo plik
            try:
                database =  json.load(new_file)
            except JSONDecodeError:
                pass
        if database:
            for item in database:
                if item["UUID"] == uuid:
                    print (item["UUID"])
                    uuid_found = True
                    return self.refine_query(item)

        if not uuid_found:
            answer = self.search_crossref(query=query, uuid=uuid)
            return self.refine_query(answer)

#example not exists
#sample_non_existing_page_url = "https://sci-hub.se/10.4324/9781003006237"
#sample usage to download
#sample_existing_page_url = "https://sci-hub.se/10.1057/9781137435026"
#print(cl.download(sample_existing_page_url))
#cl.search_crossref("BDSM")

app = Flask(__name__)
def flask_debug():
    """
    The `flask_debug` function starts a Flask application in debug mode without using the reloader.
    Do not use for production for fucks sake.
    """
    # trunk-ignore(bandit/B201)
    app.run(debug=True, use_reloader=False, host=HOST_ADDRESS, port=PORT_ADDRESS)

def waitress_run():
    """
    The `waitress_run` function serves the `app` on host "0.0.0.0"
    and port 5000 using the Waitress WSGI server.
    """
    serve(app, host=HOST_ADDRESS, port=PORT_ADDRESS)

@app.route("/query", methods=["POST"])
def query_database():
    record = json.loads(request.data)
    app.logger.info(record)
    app.logger.info(record["query"])
    app.logger.info(record["UUID"])
    cl = Librarian()
    answer_data = cl.answer_query(record["query"], record["UUID"])
    app.logger.info(answer_data)
    return_data = (
        jsonify(isError=False, message="Success", statusCode=200, data=answer_data),
        200,
    )
    return return_data

if __name__ == "__main__":
    logger = logging.getLogger('conjurer_musician')
    logger.setLevel(logging.DEBUG)
    threads = []
    #threads.append(threading.Thread(target=waitress_run))
    threads.append(threading.Thread(target=flask_debug))

    for worker in threads:
        worker.start()
    for worker in threads:
        worker.join()

#1. robi liste wyników i ją wyświetla - ale tylko tytuły niezależnych dzieł - jeśli jest coś w containerze to trza pogrupować
#2. Tworzy przyciski pozwalające sprawdzić czy jest dostępne - i jeśli jest zwraca link
#3. Jest też przycisk "more" - wyszukujemy po 100, wyświetlamy po 20. Po 100 jak jeszcze to dopiero odpytujemy crossref przyciski strzałek przesuwają po obecnej porcji danych
