class AuthorDB(db.Model):
    """
    Un auteur est une personne physique qui a écrit un livre ou qui a participé à l'élaboration du contenu d'un
    ouvrage.

    On pourrait rajouter comme informations :
    - commentaire
    - lien vers une page Wikipédia, par exemple
    -

    """
    __tablename__ = "Author"
    __table_args__ = {'sqlite_autoincrement': True}
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    first_name = db.Column(db.String(50))
    family_name = db.Column(db.String(50))
    valide = db.Column(db.Boolean, default=False)
    reference_biblio_livres = db.relationship("ReferenceBibliographiqueLivreDB", secondary=HelperAuthorBook,
                                              back_populates="authors")

    def __str__(self):
        return f"{self.first_name} {self.family_name}"

    def afficher_tooltip(self):
        return "Prénom : {}\nNom : {}".format(self.first_name, self.family_name)

    def export_to_prettify_line(self):
        return "{} ({})".format(self.family_name, self.first_name)

    def export_to_csv_line(self):
        return self.export_to_prettify_line()
class EnregistrementDB(db.Model):
    """
    Enregistrement tel que décrit dans le fichier principal qui est l'inventaire.
    C'est pour repérer un livre dans la bibliothèque.
    Les renseignements nécessaires sont :
    - description, qui est la référence bibliographique du livre
    - la côte du livre dans la bibliothèque
    - l'année d'obtention du livre
    - le nombre d'exemplaires de livres possédés
    - la provenance du livre (par un don, un achat, etc)
    - un champ texte contenant des mots-clef
    - la date de modification de la fiche
    - est-ce une fiche validée par le gestionnaire de la bibliothèque ?
        Si oui, alors la fiche est consultable par un utilisateur, si non, cela ne pourra pas être lu.
        #TODO Doit-on conserver toutes les versions des fiches ? Je crois que non, on conservera régulièrement
        des copies de la base de données.

    """
    __tablename__ = "Enregistrement"
    __table_args__ = {'sqlite_autoincrement': True}
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    # description = models.CharField(max_length=1000, verbose_name="description", null=True, default="")
    id_reference = db.Column(db.Integer, db.ForeignKey('ReferenceBibliographiqueLivre.id'))
    reference = db.relationship(ReferenceBibliographiqueLivreDB)  # , on_delete=models.CASCADE)
    commentaire = db.Column(db.String(500), default="")
    cote = db.Column(db.String(100), nullable=True, default="")
    # annee = models.IntegerField(verbose_name="année", null=True, default=2018)
    annee = db.Column(db.String(20), nullable=True, default="")
    # nb_exemplaire_supp = models.IntegerField(verbose_name="Nombre d'exemplaires supplémantaires",
    # null=True, default=0)
    nb_exemplaire_supp = db.Column(db.String(50), nullable=True, default=0)
    provenance = db.Column(db.String(100), nullable=True, default="")
    mots_clef = db.Column(db.String(100), nullable=True, default="")
    # models.ForeignKey(to=Theme, on_delete=models.SET_NULL, null=True)
    # dernier_modifieur = models.ForeignKey(to=Compte, null=True, on_delete=models.SET_NULL)
    date_modification = db.Column(db.DateTime, default=datetime.datetime.utcnow, nullable=True)
    # proprietaire = models.ForeignKey(to=Compte, on_delete=models.SET_NULL)
    valide = db.Column(db.Boolean, default=False)
    row = db.Column(db.String(500), default="")

    def __str__(self):
        return self.cote + " " + self.annee + self.nb_exemplaire_supp + \
               self.provenance + " " + self.mots_clef

    def afficher_tooltip(self):
        return """Cote : {}\nAnnée : {}\nNombre d'exemplaires : {}\nProvenance : {}""".format(
            self.cote, self.annee, self.nb_exemplaire_supp, self.provenance)

    def export_to_csv_line(self):
        line = [self.cote, self.annee, self.nb_exemplaire_supp, self.provenance,
                self.mots_clef, self.row]
        return "\t".join(line)

    @staticmethod
    def get_csv_fieldnames():
        return "\t".join(
            [ReferenceBibliographiqueLivreDB.get_csv_fieldnames(), "Cote", "Année", "N° d'exemplaires", "Provenance",
             "Mots-clef", "Ligne"])
class ReferenceBibliographiqueLivreDB(db.Model):
    """
    Un livre est caractérisé par
    - un ou plusieurs auteurs
    - un titre
    - un lieu d'édition
    - éditeur ou par défaut un imprimeur
    - nombre de pages

    On parle vraiment de ce qui est propre à un livre.

    """
    __tablename__ = "ReferenceBibliographiqueLivre"
    __table_args__ = {'sqlite_autoincrement': True}
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    authors = db.relationship(AuthorDB, secondary=HelperAuthorBook, back_populates="reference_biblio_livres")
    # id_author = db.Column(db.Integer, db.ForeignKey('author.id'))
    # author = db.relationship(AuthorDB)
    titre = db.Column(db.String(200), nullable=False)
    lieu_edition = db.Column(db.String(100), default="s.l.")
    editeur = db.Column(db.String(50), default="s.n.")
    annee = db.Column(db.String(10))
    nb_page = db.Column(db.String(10))
    valide = db.Column(db.Boolean, default=False)
    description = db.Column(db.String(500), default="")

    def __str__(self):
        return " ".join([auteur.__str__() for auteur in self.authors]) + \
               " " + self.titre + " " + self.lieu_edition + " " + self.editeur + " " + str(self.annee) + " " + \
               self.nb_page

    def afficher_tooltip(self):
        """
        Quand on survole une référence bibliographique, on affiche ces informations.
        :return:
        """
        return "{}\nTitre : {}\nLieu d'édition : {}\nEditeur : {}\nAnnée : {}\nNombre de pages : {}".format(
            " ".join([auteur.afficher_tooltip() for auteur in self.authors.all()]), self.titre, self.lieu_edition,
            self.editeur,
            str(self.annee), self.nb_page)

    def export_to_csv_line(self):
        """
        Exportation du contenu de la base en une forme lisible sous la forme d'une ligne CSV.
        :return: str
        """
        return "\t".join([", ".join([auteur.export_to_csv_line() for auteur in self.authors.all()]), self.titre,
                          self.lieu_edition, self.editeur, str(self.annee), self.nb_page, self.description.__str__()])

    @staticmethod
    def get_csv_fieldnames():
        """
        Les champs d'une ligne CSV.
        :return:
        """
        return "\t".join(["Auteur", "Titre", "Lieu d'édition", "Editeur", "Année d'édition", "N° de pages",
                          "Description"])
class EmpruntLivreDB(db.Model):
    """
    L'emprunt d'un livre est fait par un emprunteur.
    Un gestionnaire supervise un emprunt.
    Le livre emprunté est soigneusement noté.
    Il y a une date de retour du livre prévu.
    Et on note quand il revient.
    """
    __tablename__ = "EmpruntLivre"
    __table_args__ = {'sqlite_autoincrement': True}
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    id_emprunteur = db.Column(db.Integer, db.ForeignKey('User.id'))
    emprunteur = db.relationship(UserDB, primaryjoin=id_emprunteur == UserDB.id)  # , related_name="emprunteur", on_delete=models.SET_NULL, null=True)
    id_enregistrement = db.Column(db.Integer, db.ForeignKey('Enregistrement.id'))
    enregistrement = db.relationship(EnregistrementDB, primaryjoin=id_enregistrement == EnregistrementDB.id)  # , on_delete=models.SET_NULL, null=True)
    id_gestionnaire = db.Column(db.Integer, db.ForeignKey('User.id'))
    gestionnaire = db.relationship(UserDB, primaryjoin=id_gestionnaire == UserDB.id)  # , related_name="gestionnaire", on_delete=models.SET_NULL, null=True)

    commentaire = db.Column(db.String())
    emprunte = db.Column(db.Boolean, default=True)
    date_emprunt = db.Column(db.Date, default=datetime.datetime.now())
    date_retour_prevu = db.Column(db.Date)
    date_retour_reel = db.Column(db.Date)
    rendu = db.Column(db.Boolean)
class UserDB(db.Model):
    """
    Un utilisateur est quelqu'un qui peut se connecter à l'interface web.

    Un gestionnaire est un resp

    Un bibliothécaire est une personne auqui est responsable des collections

    Un éditeur est une personne qui peut exporter les données dans un format utilisable.
    """
    __tablename__ = "User"
    __table_args__ = {'sqlite_autoincrement': True}
    id = db.Column(db.Integer,
                   primary_key=True,
                   nullable=False,
                   autoincrement=True)
    first_name = db.Column(db.String(50))
    family_name = db.Column(db.String(50))
    right = db.Column(db.Enum(UserRight))

    date_inscription = db.Column(db.DateTime, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    # mdp = db.Column(db.String(80), unique=False, nullable=False)
    mdp_hash = db.Column(db.String(128), unique=False, nullable=False)
    confirmed = db.Column(db.Boolean, default=False)
    confirmed_on = db.Column(db.DateTime, nullable=True, default=None)
    is_admin = db.Column(db.Boolean, nullable=True, default=False)
    link_to_validate = db.Column(db.String(64), nullable=True, default="")

    def get_id(self):
        return self.id

    def set_password(self, password):
        self.mdp_hash = generate_password_hash(password)

    def set_confirmed(self, confirmed):
        self.confirmed = confirmed

    def set_confirmed_on(self, confirmed_on):
        self.confirmed_on = confirmed_on

    def verify_password(self, password):
        return check_password_hash(self.mdp_hash, password)

    def get_right(self):
        return self.right

    @staticmethod
    def register(email, password):
        user = UserDB(email=email)
        user.set_password(password)
        db.session.add(user)
        db.session.commit()
        return user

    @classmethod
    def authenticate(cls, **kwargs):
        email = kwargs.get("email")
        password = kwargs.get("password")
        if not email:
            print("identification a échoué")
            message = "L'adresse email ou le mot de passe est incorrect."
            return None, message
        user = cls.query.filter_by(email=email).first()

        if not password:
            message = "Aucun mot de passe."
            return None, message

        if not user:
            message = "L'adresse email est inconnue."
            return None, message

        if not user.verify_password(password):
            print("mauvais mot de passe")
            message = "L'adresse email ou le mot de passe est incorrect."
            return None, message

        if not user.confirmed:
            print("utilisateur non confirmé")
            message = "L'utilisateur n'est pas confirmé."
            return None, message

        return user, ""

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def is_authenticated(self):
        return True

    def __repr__(self):
        return '<User {0}>'.format(self.email)

    def generate_confirmation_token(self, expiration=3600):
        s = TimedJSONWebSignatureSerializer(current_app.config["SECRET_KEY"],
                                            expiration)
        return s.dumps({"confirm": self.id}).decode("utf-8")

    def confirm(self, token):
        s = TimedJSONWebSignatureSerializer(current_app.config["SECRET_KEY"])
        try:
            data = s.loads(token.encode('utf-8'))
        except:
            return False
        if data.get('confirm') != self.id:
            return False
        self.confirmed = True
        self.confirmed_on = datetime.datetime.utcnow()
        db.session.add(self)
        return True