class Func(db.Model):
    """
    :description
        專案中的View Function主表
        不需設置ForeignKey,會透過SQLAlchemy的多對多關聯機制處理

    :parameter
        func_module_name: View Function module+name
        func_description: View Function功能說明
        func_is_activate: View Function是否啟動
            default: True
        func_remark: View Function備註

    """
    __tablename__ = 'funcs'
    id = db.Column(db.Integer, primary_key=True)
    func_module_name = db.Column(db.String(50))
    func_description = db.Column(db.String(100))
    func_is_activate = db.Column(db.Boolean, default=True)
    func_remark = db.Column(db.String(100))

    def __init__(self, func_module_name, func_description, func_is_activate=True, func_remark=None):
        self.func_module_name = func_module_name
        self.func_description = func_description
        self.func_is_activate = func_is_activate
        self.func_remark = func_remark

    def __repr__(self):
        return 'id = %i, module_name = %s, is_activate = %s'
class airline(db.Model):
    __table_args__ = (PrimaryKeyConstraint('airlineID'), )
    airlineID = db.Column(db.Integer, autoincrement=True, primark_key=True)
    airlineName = db.Column(db.String(30), unique=True, nullable=False)
    airline_short = db.Column(db.String(5), unique=True, nullable=False)

    def __init__(self, name, airline_short):
        self.airlineName = name
        self.airline_short = airline_short

    def get_short(self):
        return self.airline_short
class airplacnes(db.Model):
    __table_args__ = (PrimaryKeyConstraint('airplacneID'), )
    airplacneID = db.Column(db.Integer, autoincrement=True, primark_key=True)
    airlineName = db.Column(db.String(30), unique=True)
    airplacneModel = db.Column(db.String(30), nullable=False)
    airplacneTotalSeats = db.Column(db.Integer, nullable=False)

    def __init__(self, airlineName, airplacneTotalSeats):
        self.airlineName = airlineName
        self.airplacneTotalSeats = airplacneTotalSeats

    def __repr__(self):
        return '<airplacnes> %s' % self.airlineName
class Travelling(db.Model):
    __tablename__ = 'tbl_Travelling'
    __table_args__ = (PrimaryKeyConstraint('flight'), )
    flight = db.Column(db.String(30), unique=True, nullable=False)
    from_place = db.Column(db.String(30), nullable=False)
    to_place = db.Column(db.String(30), nullable=False)
    depart_at_from = db.Column(db.DateTime, nullable=False)
    arrival_at_to = db.Column(db.DateTime, nullable=False)
    seat_no = db.Column(db.Integer, nullable=False)
    available = db.Column(db.Integer, nullable=False)
    rate = db.Column(db.Integer, nullable=False)
    off = db.Column(db.Integer, nullable=False)
    collected = db.Column(db.Integer, nullable=False)

    def __str__(self):
        return str(self.flight) + str(self.from_place) + str(
            self.to_place) + str(self.depart_at_from) + str(self.arrival_at_to)
class Flight(db.Model):
    __tablename__ = 'tbl_flight'
    __table_args__ = (
        PrimaryKeyConstraint('flightID'),
        ForeignKeyConstraint(['airlineName'], ['airline.airlineName'],
                             name='FK_airlineName'),
    )
    flightID = db.Column(db.Integer, autoincrement=True, primark_key=True)
    airlineName = db.Column(
        db.String(30),
        db.ForeignKey('airline.airlineName'),
        nullable=False,
    )
    flightNo = db.Column(db.String(30), nullable=False)
    airplacneModel = db.Column(db.String(30), nullable=False)
    from_place = db.Column(db.String(30), nullable=False)
    to_place = db.Column(db.String(30), nullable=False)
    depart_Date = db.Column(db.Date, nullable=False)
    depart_Time = db.Column(db.Time, nullable=False)
    available = db.Column(db.String(30), nullable=False)
    price = db.Column(db.Integer, nullable=False)

    airline = db.relationship("airline",
                              backref=db.backref('airline', lazy=True))
    #airport = db.relationship("Airport", backref=db.backref('Airport', lazy=True))
    #airplacnes = db.relationship("airplacnes", backref=db.backref('airplacnes', lazy=True))
    # bookings = db.relationship("UserReister", secondary="booking" , backref=db.backref('flights', lazy='dynamic') )

    @property
    def flightNo(self):
        raise AttributeError('Flight No is not a readable attribute')

    @flightNo.setter
    def flightNo(self):
        self.flightNo = airline.airline_short + self.flightID
class Role(db.Model):
    """
    權限角色主表
    不需設置ForeignKey,會透過SQLAlehemy的多對多關聯機制處理
    :parameter
        name:角色名稱
    """
    __tablename__ = 'roles'

    id = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)

    funcs = db.relationship('Func', secondary=relations_role_func, lazy='subquery',
                            backref=db.backref('roles', lazy=True))

    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name
class UserReister(UserMixin, db.Model):
    """記錄使用者資料的資料表"""
    __tablename__ = 'tbl_user'
    __table_args__ = (
        PrimaryKeyConstraint('user_id'),
    )
    user_id = db.Column(db.Integer, autoincrement=True, primark_key=True)
    user_username = db.Column(db.String(80), unique=True, nullable=False)
    user_email = db.Column(db.String(80), unique=True, nullable=False)
    # user_password = db.Column(db.String(50), nullable=False)
    user_confirm = db.Column(db.Boolean, default=False)
    password_hash = db.Column(db.String(150), nullable=False)
    user_fullname = db.Column(db.String(50))
    passportID = db.Column(db.String(20))
    passport_expiration = db.Column(db.DateTime)
    passport_country = db.Column(db.String(20))
    contactNo = db.Column(db.Integer)
    # about_me = db.Column(db.Text())

    gender = db.Column(db.Text)
    address = db.Column(db.String(20))
    regist_date = db.Column(db.DateTime, default=datetime.utcnow())
    last_login = db.Column(db.DateTime, default=datetime.utcnow())

    # flights = db.relationship("Flight", secondary="booking"  , backref=db.backref('users', lazy=True) )
    orders = db.relationship("Order", backref=db.backref('users', lazy=True))

    roles = db.relationship('Role', secondary=relations_user_role, lazy='subquery', backref=db.backref('users', lazy=True))

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = bcrypt.generate_password_hash(password).decode('utf8')

    def check_password(self, password):
        """
        密碼驗證,驗證使用者輸入的密碼跟資料庫內的加密密碼是否相符
        :param password: 使用者輸入的密碼
        :return: True/False
        """
        return bcrypt.check_password_hash(self.password_hash, password)

    def create_confirm_token(self, expires_in=3600):
        """
        利用itsdangerous來生成令牌,透過current_app來取得目前flask參數['SECRET_KEY']的值
        :param expiration: 有效時間,單位為秒
        :return: 回傳令牌,參數為該註冊用戶的id
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
        return s.dumps({'user_id': self.user_id})

    def validate_confirm_token(self, token):
        """
        驗證回傳令牌是否正確,若正確則回傳True
        :param token:驗證令牌
        :return:回傳驗證是否正確,正確為True
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)  # 驗證
        except SignatureExpired:
            #  當時間超過的時候就會引發SignatureExpired錯誤
            return False
        except BadSignature:
            #  當驗證錯誤的時候就會引發BadSignature錯誤
            return False
        return data

    def get_id(self):
        return self.user_id

    def create_reset_token(self, expires_in=3600):
        """
        提供申請遺失密碼認證使用的token
        :param expires_in: 有效時間(秒)
        :return:token
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'], expires_in=expires_in)
        return s.dumps({'reset_id': self.id})

    def check_admin(self, func_module, func_name):
        """
        檢查使用者是否有權限進入該View Function
        :param
            func_module: View Function的module
            func_name: View Function的name
        :return:
            result: 有無權限 boolean
        """
        #  取得個人的權限表
        func_list = self.user_func
        #  func的table中記錄是module+.+func_name
        view_function = func_module + '.' + func_name
        result = func_list.filter(text("func_module_name=:view_function")).params(view_function=view_function).first()
        if result:
            #flash(view_function)
            return True
        else:
            #flash(view_function)
            return False

    def has_role(self,role_name):
        role_list = self.user_role
        result = role_list.filter(text("name=:role_name")).params(role_name=role_name).first()
        if result:
            return True
        else:
            return False

    def __repr__(self):
        return 'user_username:%s, user_email:%s' % (self.user_username, self.user_email)

    @property
    def user_role(self):
        role_list = Role.query.join(relations_user_role)\
                .join(UserReister)\
                .filter(UserReister.user_id == self.user_id)
        return role_list


    @property
    def user_func(self):
        func_list = Func.query.join(relations_role_func) \
            .join(Role) \
            .join(relations_user_role) \
            .join(UserReister) \
            .filter(UserReister.user_id == self.user_id)
        return func_list
class Airport(db.Model):
    __table_args__ = (PrimaryKeyConstraint('airportID'), )
    airportID = db.Column(db.Integer, autoincrement=True, primark_key=True)
    airportNname = db.Column(db.String(30), unique=True, nullable=False)
    airport_localtion = db.Column(db.String(30), nullable=False)
class UserReister(UserMixin, db.Model):
    """記錄使用者資料的資料表"""
    __tablename__ = 'tbl_user'
    __table_args__ = (PrimaryKeyConstraint('user_id'), )
    user_id = db.Column(db.Integer, primark_key=True)
    user_username = db.Column(db.String(80), unique=True, nullable=False)
    user_email = db.Column(db.String(80), unique=True, nullable=False)
    #user_password = db.Column(db.String(50), nullable=False)
    user_confirm = db.Column(db.Boolean, default=False)
    password_hash = db.Column(db.String(50), nullable=False)

    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')

    @password.setter
    def password(self, password):
        self.password_hash = bcrypt.generate_password_hash(password).decode(
            'utf8')

    def check_password(self, password):
        """
        密碼驗證,驗證使用者輸入的密碼跟資料庫內的加密密碼是否相符
        :param password: 使用者輸入的密碼
        :return: True/False
        """
        return bcrypt.check_password_hash(self.password_hash, password)

    def create_confirm_token(self, expires_in=3600):
        """
        利用itsdangerous來生成令牌,透過current_app來取得目前flask參數['SECRET_KEY']的值
        :param expiration: 有效時間,單位為秒
        :return: 回傳令牌,參數為該註冊用戶的id
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'],
                                            expires_in=expires_in)
        return s.dumps({'user_id': self.user_id})

    def validate_confirm_token(self, token):
        """
        驗證回傳令牌是否正確,若正確則回傳True
        :param token:驗證令牌
        :return:回傳驗證是否正確,正確為True
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)  # 驗證
        except SignatureExpired:
            #  當時間超過的時候就會引發SignatureExpired錯誤
            return False
        except BadSignature:
            #  當驗證錯誤的時候就會引發BadSignature錯誤
            return False
        return data

    def get_id(self):
        return (self.user_id)

    def create_reset_token(self, expires_in=3600):
        """
        提供申請遺失密碼認證使用的token
        :param expires_in: 有效時間(秒)
        :return:token
        """
        s = TimedJSONWebSignatureSerializer(current_app.config['SECRET_KEY'],
                                            expires_in=expires_in)
        return s.dumps({'reset_id': self.id})

    def __repr__(self):
        return 'user_username:%s, user_email:%s' % (self.user_username,
                                                    self.user_email)