""" api related functions """
import json
import getpass
import requests

class APIObject:
    """ api object for api functions """
    base_url = "https://www.labii.dev"
    email = None
    password = None
    token = None
    organization__sid = None
    user = None

    def __init__(self, base_url="https://www.labii.dev", email=None, password=None, token=None, organization__sid=None, user=None):
        self.base_url = base_url
        self.email = email
        self.password = password
        self.token = token
        self.organization__sid = organization__sid
        self.user = user

    ######
    # url
    def get_list_url(self, app, model, level="organization", sid=None, serializer='list', query=""):
        """ return the list url """
        if sid is None:
            sid = self.organization__sid
        return f"/{app}/{model}/list/{level}/{sid}/{serializer}/{'' if query == '' else '?'}{query}"

    def get_detail_url(self, app, model, sid, query=""):
        """ return the detail url """
        return f"/{app}/{model}/detail/{sid}/{'' if query == '' else '?'}{query}"

    ######
    # auth
    def get_headers(self, is_authorized=True):
        """Return necessary headers for API functions

        Returns:
            dict: header objects
        """
        headers = {
            "X-Forwarded-For": "batch",
            "Content-Type": "application/json"
        }
        if is_authorized and self.token is not None:
            headers["Authorization"] = f"Token {self.token}"
        return headers

    def login(self, email="", password=""):
        """Get authentication token based on user input
        - self.login()
            - login with saved email/password
            - get email/password if not saved
        - self.login(email=xxx, password=xxx): login with new email/password

        Args:
            email (str): email to use to login
            password (str): password to use to login
        """
        # get email or password
        if email != "" and password != "":
            self.email = email
            self.password = password
        else:
            if self.email is None or self.password is None:
                self.email = input("Email: ")
                self.password = getpass.getpass('Password: ')
        # get token
        data = {
            "username": self.email,
            "password": self.password
        }
        response = self.post("/accounts/auth/", data, is_authorized=False)
        if "token" in response:
            self.token = response["token"]
            self.user = response
        elif "secret" in response:# two step authentication
            # use email to verify
            self.post("/accounts/mfa/code/", {"option": "email", "secret": response["secret"]}, is_authorized=False)
            code = input("MFA enabled for the account. Please check your email and provide authentication code: ")
            data = self.post("/accounts/mfa/verify/", {"code": code, "secret": response["secret"]}, is_authorized=False)
            if "token" in data:
                self.token = data["token"]
                self.user = data
            else:
                raise RuntimeError(data)
        else:
            raise RuntimeError(response)

    def check_token(self):
        """Check if the exist token is valid. If not valid, it will genereate a new token
            Labii token expires after 30 minutes of no activity. If your program take more than 30 minutes to run. Use this function to get a new token.
        """
        try:
            data = self.get("/accounts/checktoken/")
        except:
            #if data["detail"] != "Valid token.":
            self.login()

    ######
    # api post
    def post(self, url, data, is_authorized=True):
        """A function to do POST for labii api

        Args:
            url (str): API URL
            data (dict): data to POST, see API documentation (https://docs.labii.com/api/overview) for the format
            is_authorized (bool): should use the authrized token

        Returns:
            dict: created object
        """
        response = requests.post(url=f"{self.base_url}{url}", data=json.dumps(data, separators=(',',':')), headers=self.get_headers(is_authorized))
        try:
            data = json.loads(response.text)
            return data
        except:
            raise RuntimeError(response)

    def patch(self, url, data, is_authorized=True):
        """A function to do PATCH for labii api

        Args:
            url (str): API URL
            data (dict): data to update, see API documentation (https://docs.labii.com/api/overview) for the format
            is_authorized (bool): should use the authrized token

        Returns:
            dict: return of the api
        """
        response = requests.patch(url=f"{self.base_url}{url}", data=json.dumps(data, separators=(',',':')), headers=self.get_headers(is_authorized))
        try:
            data = json.loads(response.text)
            return data
        except:
            raise RuntimeError(response)

    def get(self, url, all_pages=False, is_authorized=True):
        """A function to do GET for labii api

        Args:
            url (str): API URL
            all (bool): should download all
            is_authorized (bool): should use the authrized token

        Returns:
            dict: result of the url
        """
        if all_pages is False:
            response = requests.get(url=f"{self.base_url}{url}", headers=self.get_headers(is_authorized))
            if response.status_code != 200:
                raise RuntimeError(response.text)
            try:
                data = json.loads(response.text)
                return data
            except:
                raise RuntimeError(response)
        else:
            page = 1
            page_size = 10
            # update url
            if "page" in url or "page_size" in url:
                query = url.split("?")[1].split("&")
                query_updated = []
                for item in query:
                    if not "page" in item and not "page_size" in item:
                        query_updated.append(item)
                if len(query_updated) > 0:
                    url = f"{url.split('?')[0]}?{'&'.join(query_updated)}"
                else:
                    url = url.split("?")[0]
            if "?" in url:
                url_with_page = f"{url}&page={page}&page_size={page_size}"
            else:
                url_with_page = f"{url}?page={page}&page_size={page_size}"
            response = requests.get(url=f"{self.base_url}{url_with_page}", headers=self.get_headers(is_authorized))
            if response.status_code != 200:
                raise RuntimeError(response.text)
            data = json.loads(response.text)
            count = data["count"]
            results = data["results"]
            print(f"Total records: {count}")
            print("Downloading page 1...")
            while len(results) < count:
                page += 1
                print(f"Downloading page {page}...")
                if "?" in url:
                    url_with_page = f"{url}&page={page}&page_size={page_size}"
                else:
                    url_with_page = f"{url}?page={page}&page_size={page_size}"
                response = requests.get(url=f"{self.base_url}{url_with_page}", headers=self.get_headers(is_authorized))
                results = results + json.loads(response.text)["results"]
            data["results"] = results
            return data

    def delete(self, url, is_authorized=True):
        """A function to do delete for labii api

        Args:
            url (str): API URL
            is_authorized (bool): should use the authrized token

        Returns:
            dict: result of the url
        """
        response = requests.delete(url=f"{self.base_url}{url}", headers=self.get_headers(is_authorized))
        if response.status_code != 200:
            raise RuntimeError(response.text)

#
# def labii_json_list_to_tsv(results):
#     """conver the list result in json format into tsv format
#
#     Args:
#         results (array): the results from response
#
#     Returns:
#         string: tsv data
#     """
#     if len(results) > 0:
#         should_collect_title = True
#         titles = [] # collect list of titles
#         data = [] # the data of all
#         for r in results:
#             d = [] # the data of one row
#             for field in r:
#                 if field == "column_set":
#                     for f in r["column_set"]:
#                         d.append(str(f["data"]).replace("\n","").replace("\r",""))
#                         if should_collect_title:
#                             titles.append(f["column"]["name"])
#                 elif field != "section_set":
#                     d.append(str(r[field]).replace("\n","").replace("\r",""))
#                     if should_collect_title:
#                         titles.append(field)
#             if should_collect_title:
#                 data.append("\t".join(titles))
#                 should_collect_title = False
#             data.append("\t".join(d))
#         return "\n".join(data)
#     else:
#         return "";
