import os
import sys

from sqlalchemy import (
    Column,
    DateTime,
    Integer,
    MetaData,
    String,
    Table,
    Text,
    create_engine,
)
from sqlalchemy.orm import mapper, sessionmaker

sys.path.append("../../..")
import datetime

from common.variables import *


# Класс - база данных сервера.
class ClientDatabase:
    # Класс - отображение таблицы известных пользователей.
    class KnownUsers:
        def __init__(self, user):
            self.id = None
            self.username = user

    # Класс - отображение таблицы истории сообщений
    class MessageHistory:
        def __init__(self, contact, direction, message):
            self.id = None
            self.contact = contact
            self.direction = direction
            self.message = message
            self.date = datetime.datetime.now()

    # Класс - отображение списка контактов
    class Contacts:
        def __init__(self, contact):
            self.id = None
            self.name = contact

    # Конструктор класса:
    def __init__(self, name):
        # Создаём движок базы данных, поскольку разрешено несколько клиентов одновременно,
        # то каждый должен иметь свою БД.
        # Поскольку клиент мультипоточный,
        # то необходимо отключить проверки на подключения с разных потоков,
        # иначе sqlite3.ProgrammingError
        path = os.path.dirname(os.path.realpath(__file__))
        filename = f"client_{name}.db3"
        self.database_engine = create_engine(
            f"sqlite:///{os.path.join(path, filename)}",
            echo=False,
            pool_recycle=7200,
            connect_args={"check_same_thread": False},
        )

        # Создаём объект MetaData
        self.metadata = MetaData()

        # Создаём таблицу известных пользователей
        users = Table(
            "known_users",
            self.metadata,
            Column("id", Integer, primary_key=True),
            Column("username", String),
        )

        # Создаём таблицу истории сообщений
        history = Table(
            "message_history",
            self.metadata,
            Column("id", Integer, primary_key=True),
            Column("contact", String),
            Column("direction", String),
            Column("message", Text),
            Column("date", DateTime),
        )

        # Создаём таблицу контактов
        contacts = Table(
            "contacts",
            self.metadata,
            Column("id", Integer, primary_key=True),
            Column("name", String, unique=True),
        )

        # Создаём таблицы
        self.metadata.create_all(self.database_engine)

        # Создаём отображения
        mapper(self.KnownUsers, users)
        mapper(self.MessageHistory, history)
        mapper(self.Contacts, contacts)

        # Создаём сессию
        Session = sessionmaker(bind=self.database_engine)
        self.session = Session()

        # Необходимо очистить таблицу контактов, т.к. при запуске они подгружаются с сервера.
        self.session.query(self.Contacts).delete()
        self.session.commit()

    # Функция добавления контактов
    def add_contact(self, contact):
        if (
            not self.session.query(self.Contacts)
            .filter_by(name=contact)
            .count()
        ):
            contact_row = self.Contacts(contact)
            self.session.add(contact_row)
            self.session.commit()

    # Функция удаления контакта
    def del_contact(self, contact):
        self.session.query(self.Contacts).filter_by(name=contact).delete()
        self.session.commit()

    # Функция добавления известных пользователей.
    # Пользователи получаются только с сервера, поэтому таблица очищается.
    def add_users(self, users_list):
        self.session.query(self.KnownUsers).delete()
        for user in users_list:
            user_row = self.KnownUsers(user)
            self.session.add(user_row)
        self.session.commit()

    # Функция, сохраняющая сообщения
    def save_message(self, contact, direction, message):
        message_row = self.MessageHistory(contact, direction, message)
        self.session.add(message_row)
        self.session.commit()

    # Функция, возвращающая контакты
    def get_contacts(self):
        return [
            contact[0]
            for contact in self.session.query(self.Contacts.name).all()
        ]

    # Функция, возвращающая список известных пользователей
    def get_users(self):
        return [
            user[0]
            for user in self.session.query(self.KnownUsers.username).all()
        ]

    # Функция, проверяющая наличие пользователя в известных
    def check_user(self, user):
        if self.session.query(self.KnownUsers).filter_by(username=user).count():
            return True
        else:
            return False

    # Функция, проверяющая наличие пользователя контактах
    def check_contact(self, contact):
        if self.session.query(self.Contacts).filter_by(name=contact).count():
            return True
        else:
            return False

    # Функция, возвращающая историю переписки
    def get_history(self, contact):
        query = self.session.query(self.MessageHistory).filter_by(
            contact=contact
        )
        return [
            (
                history_row.contact,
                history_row.direction,
                history_row.message,
                history_row.date,
            )
            for history_row in query.all()
        ]


# отладка
if __name__ == "__main__":
    test_db = ClientDatabase("test1")
    for i in ["test3", "test4", "test5"]:
        test_db.add_contact(i)
    test_db.add_contact("test4")
    test_db.add_users(["test1", "test2", "test3", "test4", "test5"])
    test_db.save_message(
        "test2",
        "in",
        f"Привет! я тестовое сообщение от {datetime.datetime.now()}!",
    )
    test_db.save_message(
        "test2",
        "out",
        f"Привет! я другое тестовое сообщение от {datetime.datetime.now()}!",
    )
    print(test_db.get_contacts())
    print(test_db.get_users())
    print(test_db.check_user("test1"))
    print(test_db.check_user("test10"))
    print(sorted(test_db.get_history("test2"), key=lambda item: item[3]))
    test_db.del_contact("test4")
    print(test_db.get_contacts())
