import jwt
import time
import logging
import sqlite3 as sqlite
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import and_,or_
from be.model import error
from be.model import postgreSQLORM
from be.model.postgreSQLORM import User
from be.model import db_conn
from be.model.postgreSQLORM import Book
from be.model.postgreSQLORM import Search_author
from be.model.postgreSQLORM import Search_tags
from be.model.postgreSQLORM import Search_book_intro
from be.model.postgreSQLORM import Search_title
# encode a json string like:
#   {
#       "user_id": [user name],
#       "terminal": [terminal code],
#       "timestamp": [ts]} to a JWT
#   }


def jwt_encode(user_id: str, terminal: str) -> str:
    encoded = jwt.encode(
        {"user_id": user_id, "terminal": terminal, "timestamp": time.time()},
        key=user_id,
        algorithm="HS256",
    )
    return encoded.encode("utf-8").decode("utf-8")


# decode a JWT to a json string like:
#   {
#       "user_id": [user name],
#       "terminal": [terminal code],
#       "timestamp": [ts]} to a JWT
#   }
def jwt_decode(encoded_token, user_id: str) -> str:
    decoded = jwt.decode(encoded_token, key=user_id, algorithms="HS256")
    return decoded


class User(db_conn.DBConn):
    token_lifetime: int = 3600  # 3600 second

    def __init__(self):
        db_conn.DBConn.__init__(self)

    def __check_token(self, user_id, db_token, token) -> bool:
        try:
            if db_token != token:
                # print('touch')
                return False
            jwt_text = jwt_decode(encoded_token=token, user_id=user_id)
            ts = jwt_text["timestamp"]
            if ts is not None:
                now = time.time()
                if self.token_lifetime > now - ts >= 0:
                    return True
        except jwt.exceptions.InvalidSignatureError as e:
            logging.error(str(e))
            return False

    def register(self, user_id: str, password: str):
        ## 判断用户是否注册过了
        if self.user_id_exist(user_id=user_id):
            return error.error_exist_user_id(user_id)
        else:
        # try:
            terminal = "terminal_{}".format(str(time.time()))
            token = jwt_encode(user_id, terminal)
            ## 为新注册的用户创建对象
            new_user = postgreSQLORM.User(user_id=user_id,password=password,balance=0,token=token,terminal=terminal)
            self.session.add(new_user)
            self.session.commit()
            
            # self.conn.execute(
            #     "INSERT into user(user_id, password, balance, token, terminal) "
            #     "VALUES (?, ?, ?, ?, ?);",
            #     (user_id, password, 0, token, terminal), )
            # self.conn.commit()
        # except sqlite.Error:
        #     return error.error_exist_user_id(user_id)
        return 200, "ok"

    def check_token(self, user_id: str, token: str) -> (int, str):
        row = self.session.query(postgreSQLORM.User.token).filter(postgreSQLORM.User.user_id==user_id).first()
        # cursor = self.conn.execute("SELECT token from user where user_id=?", (user_id,))
        # row = cursor.fetchone()
        # print(row)
        if row is None:
            # print('touch')
            return error.error_authorization_fail()
        db_token = row[0]
        # print(db_token)
        # print(token)
        if not self.__check_token(user_id, db_token, token):
            # print('touch')
            return error.error_authorization_fail()
        return 200, "ok"

    def check_password(self, user_id: str, password: str) -> (int, str):
        row = self.session.query(postgreSQLORM.User.password).filter(postgreSQLORM.User.user_id==user_id).first()
        # cursor = self.conn.execute("SELECT password from user where user_id=?", (user_id,))
        # row = cursor.fetchone()
        if row is None:
            return error.error_authorization_fail()

        if password != row[0]:
            return error.error_authorization_fail()

        return 200, "ok"

    def login(self, user_id: str, password: str, terminal: str) -> (int, str, str):
        token = ""
        try:
            code, message = self.check_password(user_id, password)
            if code != 200:
                return code, message, ""

            token = jwt_encode(user_id, terminal)
            row = self.session.query(postgreSQLORM.User).filter_by(user_id=user_id).update({'token':token,'terminal':terminal})
            
            # cursor = self.conn.execute(
            #     "UPDATE user set token= ? , terminal = ? where user_id = ?",
            #     (token, terminal, user_id), )
            # if cursor.rowcount == 0:
            if row == 0:
                return error.error_authorization_fail() + ("", )
            # self.conn.commit()
            self.session.commit()
        except SQLAlchemyError as e:
            return 528, "{}".format(str(e)), ""
        except BaseException as e:
            return 530, "{}".format(str(e)), ""
        return 200, "ok", token

    def logout(self, user_id: str, token: str) -> bool:
        try:
            code, message = self.check_token(user_id, token)
            if code != 200:
                return code, message

            terminal = "terminal_{}".format(str(time.time()))
            dummy_token = jwt_encode(user_id, terminal)
            
            row = self.session.query(postgreSQLORM.User).filter_by(user_id=user_id).update({'token':dummy_token,'terminal':terminal})
            # cursor = self.conn.execute(
            #     "UPDATE user SET token = ?, terminal = ? WHERE user_id=?",
            #     (dummy_token, terminal, user_id), )
            # if cursor.rowcount == 0:
            # print(row)
            if row == 0:
                return error.error_authorization_fail()

            # self.conn.commit()
            self.session.commit()
        except SQLAlchemyError as e:
            return 528, "{}".format(str(e))
        except BaseException as e:
            return 530, "{}".format(str(e))
        return 200, "ok"

    def unregister(self, user_id: str, password: str) -> (int, str):
        try:
            code, message = self.check_password(user_id, password)
            if code != 200:
                return code, message

            row = self.session.query(postgreSQLORM.User).filter(postgreSQLORM.User.user_id==user_id).delete()
            # cursor = self.conn.execute("DELETE from user where user_id=?", (user_id,))
            # if cursor.rowcount == 1:
            if row == 1:
                self.session.commit()
                # self.conn.commit()
            else:
                return error.error_authorization_fail()
        except SQLAlchemyError as e:
            return 528, "{}".format(str(e))
        except BaseException as e:
            return 530, "{}".format(str(e))
        return 200, "ok"

    def change_password(self, user_id: str, old_password: str, new_password: str) -> bool:
        try:
            code, message = self.check_password(user_id, old_password)
            if code != 200:
                return code, message

            terminal = "terminal_{}".format(str(time.time()))
            token = jwt_encode(user_id, terminal)
            row = self.session.query(postgreSQLORM.User).filter_by(user_id=user_id).update({'password':new_password,'token':token,'terminal':terminal})
            # cursor = self.conn.execute(
            #     "UPDATE user set password = ?, token= ? , terminal = ? where user_id = ?",
            #     (new_password, token, terminal, user_id), )
            # if cursor.rowcount == 0:
            if row == 0:
                return error.error_authorization_fail()
            self.session.commit()
            # self.conn.commit()
        except SQLAlchemyError as e:
            return 528, "{}".format(str(e))
        except BaseException as e:
            return 530, "{}".format(str(e))
        return 200, "ok"

    def search_author(self, author:str,page:int)-> (int,[dict]):#200,'ok',list[{str,str,str,str,list,bytes}]
        ret=[]
        # select
        # title, author, publisher, book_intro, tags, picture
        # from book where
        # book_id in
        # (select book_id from search_tags where tags='小说' and search_id BETWEEN 20 and 30)
        #已付款
        row = self.session.query(Search_author.book_id).filter(and_(Search_author.author==author,Search_author.search_id<=10*page-1,Search_author.search_id<=10*page-10)).all()
        if len(row)!=0:
            for i in row:
                row_tmp = self.session.query(Book).filter(Book.book_id==i).first()
                if row_tmp is None:
                    continue
                else:
                    title=row_tmp.title
                    author_ = row_tmp.author
                    publisher = row_tmp.publisher
                    book_intro = row_tmp.book_intro
                    tags = row_tmp.tags
                
                    ret.append(
                        {'title': title, 'author': author_, 'publisher': publisher,
                         'book_intro': book_intro,
                         'tags': tags})
            return 200,  ret
        else:
            return 200,  []

    def search_book_intro(self, book_intro:str,page:int)-> (int,[dict]):
        ret = []
        row = self.session.query(Search_book_intro.book_id).filter(and_(Search_book_intro.book_intro==book_intro,Search_book_intro.search_id<=10*page-1,Search_author.search_id<=10*page-10)).all()
        if len(row)!=0:
            for i in row:
                row_tmp = self.session.query(Book).filter(Book.book_id==i).first()
                if row_tmp is None:
                    continue
                else:
                    title=row_tmp.title
                    author_ = row_tmp.author
                    publisher = row_tmp.publisher
                    book_intro = row_tmp.book_intro
                    tags = row_tmp.tags
                
                    ret.append(
                        {'title': title, 'author': author_, 'publisher': publisher,
                         'book_intro': book_intro,
                         'tags': tags})
            return 200,  ret
        else:
            return 200,  []

    def search_tags(self, tags:str,page:int)-> (int,[dict]):
        ret = []
        row = self.session.query(Search_tags.book_id).filter(and_(Search_tags.tags==tags,Search_tags.search_id<=10*page-1,Search_tags.search_id<=10*page-10)).all()
        if len(row)!=0:
            for i in row:
                row_tmp = self.session.query(Book).filter(Book.book_id==i).first()
                if row_tmp is None:
                    continue
                else:
                    title=row_tmp.title
                    author_ = row_tmp.author
                    publisher = row_tmp.publisher
                    book_intro = row_tmp.book_intro
                    tags = row_tmp.tags
                
                    ret.append(
                        {'title': title, 'author': author_, 'publisher': publisher,
                         'book_intro': book_intro,
                         'tags': tags})
            return 200,  ret
        else:
            return 200,  []

    def search_title(self, title:str,page:int)-> (int,[dict]):
        ret = []
        row = self.session.query(Search_title.book_id).filter(and_(Search_title.title==title,Search_title.search_id<=10*page-1,Search_author.search_id<=10*page-10)).all()
        if len(row)!=0:
            for i in row:
                row_tmp = self.session.query(Book).filter(Book.book_id==i).first()
                if row_tmp is None:
                    continue
                else:
                    title=row_tmp.title
                    author_ = row_tmp.author
                    publisher = row_tmp.publisher
                    book_intro = row_tmp.book_intro
                    tags = row_tmp.tags
                
                    ret.append(
                        {'title': title, 'author': author_, 'publisher': publisher,
                         'book_intro': book_intro,
                         'tags': tags})
            return 200,  ret
        else:
            return 200,  []

