#include "csqliteauthoritymanager.h"
#include <QSqlError>
#include <algorithm>

enum ErrCode {
	kNoErr=0,
	kInternalErr,
	kDataErr,
	kInitFailed,
	kUserNotExist,
	kUserDisabled,
	kUserAlreadyExist,
	kRoleAlreadyExist,
	kPermissionTagNameAlreadyExist,
	kMaxErr,
};
const char* ErrString[] =
{
	"NoErr",
	"Inner Data Error",
	"Data "
	"Init Failed",
	"UserNotExist",
	"UserDisabled",
	"UserAlreadyExist",
	"RoleAlreadyExist",
	"PermissionAlreadyExist",
};

std::string CSqliteAuthorityManager::GetPermissionTag(int iPermissionId)
{
	QSqlQuery query;
	query.prepare("select * from permissions where id=:permissionId");
	query.bindValue(":permissionId", iPermissionId);

	if (!query.exec())
	{
		m_LastError = kInternalErr;
		return "";
	}

	if (query.next())
	{
		return query.value("tag").toString().toStdString();
	}
	else
	{
		return "";
	}
}

int CSqliteAuthorityManager::GetPermissionId(std::string sPermissionTag)
{
    throw std::logic_error("The method or operation is not implemented.");
}

bool CSqliteAuthorityManager::HasPermission(int iPermissionId)
{
    throw std::logic_error("The method or operation is not implemented.");
}

bool CSqliteAuthorityManager::HasPermission(std::string sPermissionTag)
{
    if (m_mapPermissionTag.empty())return false;

    return m_mapPermissionTag.find(sPermissionTag) != m_mapPermissionTag.end();
}

bool CSqliteAuthorityManager::HasPermission(std::vector<std::string> vecPermissionList)
{
    throw std::logic_error("The method or operation is not implemented.");
}

bool CSqliteAuthorityManager::Login(const std::string loginName, const std::string password)
{
	m_bHasLogin = false;
    m_mapPermissionTag.clear();
    bool bExist = false;
    if (CheckExistUser(loginName, bExist)) {
		if (!bExist)
		{
			return false;
		}
    }

    QSqlQuery query;
    query.prepare("select * from users where name=:loginName and password=:password");
    query.bindValue(":loginName", loginName.c_str());
    query.bindValue(":password", password.c_str());

    if (!query.exec()) {
        if (LoginUserChanged) {
            LoginUserChanged();
        }
		m_LastError = kInternalErr;
        return false;
    }

    if (query.next()) {
        User user;
        user.ID = query.value("id").toInt();
        user.LoginName = query.value("name").toString().toStdString();
        user.DisplayName = query.value("display_name").toString().toStdString();

        user.Disabled = query.value("disabled").toInt() == 1;
        user.Removed = query.value("removed").toInt() == 1;

        if (user.Removed || user.Disabled) {
			m_LastError = kUserDisabled;

            if (LoginUserChanged) {
                LoginUserChanged();
            }
            return false;
        }

        m_LoginUser = user;

        std::map<int, Permission> mapPermission = GetPermissionsByUser(m_LoginUser.ID);
        
        for (auto iter : mapPermission) {
            m_mapPermissionTag.emplace(std::make_pair(iter.second.TagName, iter.second));
        }
    } else {
        if (LoginUserChanged) {
            LoginUserChanged();
        }
        return false;
    }

	m_bHasLogin = true;
    if (LoginUserChanged) {
        LoginUserChanged();
    }
    return true;
}

bool CSqliteAuthorityManager::HasLogin()
{
	return m_bHasLogin;
}

User CSqliteAuthorityManager::GetLoginUser()
{
    return m_LoginUser;
}

std::map<int, std::string> CSqliteAuthorityManager::GetPermissionList()
{
    throw std::logic_error("The method or operation is not implemented.");
}

std::string CSqliteAuthorityManager::GetLastError()
{
	int maxErr = sizeof(ErrString);
	if (m_ErrCode >= (int)kMaxErr || m_ErrCode<(int)kNoErr)
	{
		m_LastError = ErrString[kDataErr];
	}
	else
	{
		m_LastError = ErrString[m_ErrCode];
	}

	return m_LastError;
}

std::map<int, int> CSqliteAuthorityManager::GetRolesByUser(int userId)
{
    std::map<int, int> vecRoles;
    QSqlQuery query;
    query.prepare("select role_id from role_user where user_id=:uerId");
    query.bindValue(":uerId", userId);
    if (!query.exec()) {
        return vecRoles;
    }

    while (query.next()) {
        int roleId = query.value("role_id").toInt();
        vecRoles.emplace(std::make_pair(roleId, userId));
    }

    return vecRoles;
}

std::map<int, Permission> CSqliteAuthorityManager::GetPermissions()
{
    QSqlTableModel* sqlTableModel = m_ModelPermission;
    m_mapPermission.clear();
    sqlTableModel->select();
    for (int i = 0; i < sqlTableModel->rowCount(); ++i) {
        Permission permission;
        QVariant data = sqlTableModel->data(sqlTableModel->index(i, 0));
        permission.ID = data.toInt();

        data = sqlTableModel->data(sqlTableModel->index(i, 1));
        permission.Name = data.toString().toStdString();

        data = sqlTableModel->data(sqlTableModel->index(i, 2));
        permission.TagName = data.toString().toStdString();

        data = sqlTableModel->data(sqlTableModel->index(i, 3));
        permission.Description = data.toString().toStdString();

        data = sqlTableModel->data(sqlTableModel->index(i, 4));
        permission.Disabled = data.toInt() == 1;

        m_mapPermission.emplace(std::make_pair(permission.ID, permission));
    }

    return m_mapPermission;
}

std::map<int, Permission> CSqliteAuthorityManager::GetPermissionsByUser(int userId)
{

    QSqlQuery query;
    query.prepare(R"(select distinct p.* from permissions as p
				left join role_user as ru on ru.user_id
				join role_permission  as rp on rp.role_id=ru.role_id and rp.permission_id=p.id
				where ru.user_id=:userId;)");
    query.bindValue(":userId", userId);

    std::map<int, Permission> mapPermission;
    if (!query.exec()) {
        return mapPermission;
    }

    while (query.next()) {
        Permission permission;
        permission.ID = query.value("id").toInt();
        permission.TagName = query.value("tag").toString().toStdString();
        permission.Description = query.value("description").toString().toStdString();
        permission.Disabled = query.value("disabled").toInt() == 1 ? true : false;

        if (!permission.Disabled) {
            mapPermission.emplace(std::make_pair(permission.ID, permission));
        }
    }

    return mapPermission;
}

std::map<int, int> CSqliteAuthorityManager::GetPermissionsByRole(int roleId)
{
    std::map<int, int> vecList;
    QSqlQuery query;
    query.prepare("select permission_id from role_permission where role_id=:roleId");
    query.bindValue(":roleId", roleId);
    if (!query.exec()) {
        return vecList;
    }

    while (query.next()) {
        int permissionId = query.value("permission_id").toInt();
        vecList.emplace(std::make_pair(permissionId, roleId));
    }

    return vecList;
}

std::map<int, User> CSqliteAuthorityManager::GetUsers(bool bAll/* = false*/)
{
    m_mapUsers.clear();
    m_ModelUser->select();
    for (int i = 0; i < m_ModelUser->rowCount(); ++i) {
        User user;
        QVariant data = m_ModelUser->data(m_ModelUser->index(i, 0));
        user.ID = data.toInt();
        data = m_ModelUser->data(m_ModelUser->index(i, 1));
        user.LoginName = data.toString().toStdString();

        data = m_ModelUser->data(m_ModelUser->index(i, 2));
        user.DisplayName = data.toString().toStdString();
        if (bAll) {
            data = m_ModelUser->data(m_ModelUser->index(i, 3));
            user.Password = data.toString().toStdString();
        } else {
            user.Password = "";
        }


        data = m_ModelUser->data(m_ModelUser->index(i, 4));
        user.Disabled = data.toInt() == 1;
        if (user.Disabled && !bAll)continue;

        data = m_ModelUser->data(m_ModelUser->index(i, 5));
        user.Removed = data.toInt() == 1;
        if (user.Removed && !bAll)continue;

        m_mapUsers.emplace(std::make_pair(user.ID, user));
    }

    return m_mapUsers;
}

std::map<int, Role> CSqliteAuthorityManager::GetRoles()
{
    m_ModelRole->select();
    m_mapRoles.clear();
    for (int i = 0; i < m_ModelRole->rowCount(); ++i) {
        Role role;
        QVariant data = m_ModelRole->data(m_ModelRole->index(i, 0));
        role.ID = data.toInt();
        data = m_ModelRole->data(m_ModelRole->index(i, 1));
        role.Name = data.toString().toStdString();

        data = m_ModelRole->data(m_ModelRole->index(i, 0));
        role.Level = data.toInt();

        data = m_ModelRole->data(m_ModelRole->index(i, 2));
        role.Description = data.toString().toStdString();

        data = m_ModelRole->data(m_ModelRole->index(i, 4));
        role.Disabled = data.toInt() == 1;

        data = m_ModelRole->data(m_ModelRole->index(i, 5));
        role.Removed = data.toInt() == 1;

        m_mapRoles.emplace(std::make_pair(role.ID, role));
    }

    return m_mapRoles;
}

bool CSqliteAuthorityManager::AddUser(const User& user)
{
    bool bExist = true;
    if (!CheckExistUser(user.LoginName, bExist)) {
        return false;
    }

    if (bExist) {
        return false;
    }

    QSqlQuery query;
    query.prepare("insert into users (name, display_name, password) values(:loginName, :dispName, :pwd)");
    query.bindValue(":loginName", user.LoginName.c_str());
    query.bindValue(":dispName", user.DisplayName.c_str());
    query.bindValue(":pwd", user.Password.c_str());
    return query.exec();
}

bool CSqliteAuthorityManager::DeleteUser(int userId, bool bSetRemoveTag/*=false*/)
{
    QSqlQuery query;
    if (bSetRemoveTag) {
        query.prepare("update users set removed=1 where id=:userId");
    } else {
        query.prepare("delete from users where id=:userId");
    }

    query.bindValue(":userId", userId);
    if (!query.exec()) {
        return false;
    }
    return query.numRowsAffected();
}

bool CSqliteAuthorityManager::DeleteUser(const std::string loginName)
{
    return false;
}

bool CSqliteAuthorityManager::EnableUser(int userId, bool bEnable)
{
    QSqlQuery query;

    query.prepare("update users set disabled=:disable where id=:userId");
    query.bindValue(":userId", userId);
    query.bindValue(":disable", bEnable ? 0 : 1);
    if (!query.exec()) {
        return false;
    }
    return query.numRowsAffected();
}

bool CSqliteAuthorityManager::MotifyUser(const User& user, bool bPassword /*= false*/)
{
    bool bExist = true;
    if (!CheckExistUser(user.LoginName, bExist)) {
        return false;
    }

    if (!bExist)return false;

    QSqlQuery query;
    if (bPassword) {
        query.prepare("update users set display_name=:dispName, password=:pwd, disabled=:disabled where name=:loginName");
        query.bindValue(":pwd", user.Password.c_str());
    } else {
        query.prepare("update users set display_name=:dispName, disabled=:disabled where name=:loginName");
    }

    query.bindValue(":loginName", user.LoginName.c_str());
    query.bindValue(":dispName", user.DisplayName.c_str());
    query.bindValue(":disabled", user.Disabled ? 1 : 0);
    if (!query.exec()) {
        MqDebug() << query.lastError().databaseText() << query.lastError().text();
        MqDebug() << query.lastQuery();
        return false;
    }

    return true;
}

bool CSqliteAuthorityManager::AddRole(const Role& role)
{
    bool bExist = true;
    if (CheckExistRole(role.Name, bExist)) {
        return false;
    }

    QSqlQuery query;
    query.prepare("insert into roles (name, description) values(:loginName, :roleName)");
    query.bindValue(":roleName", role.Name.c_str());
    query.bindValue(":description", role.Description.c_str());
    return query.exec();
}

bool CSqliteAuthorityManager::DeleteRole(int roleId)
{
    QSqlQuery query;
    query.prepare("delete from roles where id=:roleId");

    query.bindValue(":roleId", roleId);
    if (!query.exec()) {
        return false;
    }
    return query.numRowsAffected();
}

bool CSqliteAuthorityManager::MotifyRole(const Role& role)
{
    bool bExist = true;
    if (CheckExistRole(role.Name, bExist)) {
        return false;
    }

    if (!bExist)return false;

    QSqlQuery query;
    query.prepare("update into roles (name, description, level, disabled) values(:roleName, :description, :level, :disabled)");
    query.bindValue(":roleName", role.Name.c_str());
    query.bindValue(":description", role.Description.c_str());
    query.bindValue(":level", role.Level);
    query.bindValue(":disabled", role.Disabled ? 1 : 0);
    return query.exec();
}

bool CSqliteAuthorityManager::AddPermission(const Permission& permission)
{
    QSqlQuery query;
    query.prepare("insert into permissions (name, tag, description) values(:Name, :tagName, :description)");
    query.bindValue(":Name", permission.Name.c_str());
    query.bindValue(":tagName", permission.TagName.c_str());
    query.bindValue(":description", permission.Description.c_str());
    return query.exec();
}

bool CSqliteAuthorityManager::DeletePermission(int permissionId)
{
    QSqlQuery query;
    query.prepare("delete from permissions where id=:permissionId");

    query.bindValue(":permissionId", permissionId);
    if (!query.exec()) {
        return false;
    }
    return query.numRowsAffected();
}

bool CSqliteAuthorityManager::MotifyPermission(const Permission& permission)
{

	bool bExist = true;

	std::string tagName = GetPermissionTag(permission.ID);

	if(tagName != permission.TagName){
		if (!CheckExistPermission(permission.TagName, bExist))
		{
			m_ErrCode = kPermissionTagNameAlreadyExist;
			return false;
		}
	}

	if (!bExist)return false;

	QSqlQuery query;
	query.prepare("update permissions set name=:name, tag=:tagName, description=:description where id=:permissionId");
	query.bindValue(":name", permission.Name.c_str());
	query.bindValue(":tagName", permission.TagName.c_str());
	query.bindValue(":description", permission.Description.c_str());
	query.bindValue(":permissionId", permission.ID);

	if (!query.exec())
	{
		MqDebug() << query.lastError().databaseText() << query.lastError().text();
		MqDebug() << query.lastQuery();
		m_ErrCode = kInternalErr;
		return false;
	}

	return true;
}

bool CSqliteAuthorityManager::UpdateUserRoles(int userId, std::vector<int> roleIdList)
{
    std::map<int, int> roleRelationUser = GetRolesByUser(userId);
    std::vector<int> srcList;

    std::vector<int>& dstList = roleIdList;
    std::vector<int> removeList;
    std::vector<int> addList;
    for (const auto& var : roleRelationUser) {
        srcList.push_back(var.first);
    }

    std::sort(srcList.begin(), srcList.end());
    std::sort(dstList.begin(), dstList.end());

    std::set_difference(dstList.begin(), dstList.end(), srcList.begin(), srcList.end(), std::back_inserter(addList));
    std::set_difference(srcList.begin(), srcList.end(), dstList.begin(), dstList.end(), std::back_inserter(removeList));

    if (!removeList.size() && !addList.size()) {
        return true;
    }

    QSqlDatabase::database().transaction();
    //删除
    if (removeList.size()) {

        QStringList idList;
        for each (int id in removeList) {
            idList << QString::number(id);
        }

        QSqlQuery query;
        QString sList = idList.join(",");
        query.prepare(QString("delete from role_user where user_id=:userId and role_id in(%1)").arg(sList));
        query.bindValue(":userId", userId);
        //HACK:不能使用绑定变量的方式放到in里面
        //query.bindValue(":roleIdList", idList2);

        if (!query.exec()) {
            MqDebug() << query.lastError().databaseText() << query.lastError().text();
            MqDebug() << query.lastQuery();
            QSqlDatabase::database().rollback();
            return false;
        }
        int iRows = query.numRowsAffected();
    }

    //增加
    if (addList.size()) {
        QVariantList idList;
        QVariantList userIdList;
        for each (int id in addList) {
            userIdList << userId;
            idList << id;

            QSqlQuery query;
            query.prepare("insert into role_user(role_id,user_id) values(:role_id,:user_id)");
            query.bindValue(":role_id", id);
            query.bindValue(":user_id", userId);

            if (!query.exec()) {
                QSqlDatabase::database().rollback();

                MqDebug() << query.lastError().databaseText() << query.lastError().text();
                MqDebug() << query.lastQuery();
                return false;
            }
        }
    }

    QSqlDatabase::database().commit();

    return true;
}

bool CSqliteAuthorityManager::UpdateRolePermissions(int roleId, std::vector<int> permissionIdList)
{
    std::map<int, int> roleRelationUser = GetPermissionsByRole(roleId);
    std::vector<int> srcList;

    std::vector<int>& dstList = permissionIdList;
    std::vector<int> removeList;
    std::vector<int> addList;
    for (const auto& var : roleRelationUser) {
        srcList.push_back(var.first);
    }

    std::sort(srcList.begin(), srcList.end());
    std::sort(dstList.begin(), dstList.end());

    std::set_difference(dstList.begin(), dstList.end(), srcList.begin(), srcList.end(), std::back_inserter(addList));
    std::set_difference(srcList.begin(), srcList.end(), dstList.begin(), dstList.end(), std::back_inserter(removeList));

    if (!removeList.size() && !addList.size()) {
        return true;
    }

    QSqlDatabase::database().transaction();
    //删除
    if (removeList.size()) {

        QStringList idList;
        for each (int id in removeList) {
            idList << QString::number(id);
        }

        QSqlQuery query;
        QString sList = idList.join(",");
        query.prepare(QString("delete from role_permission where role_id=:roleId and permission_id in(%1)").arg(sList));
        query.bindValue(":roleId", roleId);
        //HACK:不能使用绑定变量的方式放到in里面
        //query.bindValue(":roleIdList", idList2);

        if (!query.exec()) {
            MqDebug() << query.lastError().databaseText() << query.lastError().text();
            MqDebug() << query.lastQuery();
            QSqlDatabase::database().rollback();
            return false;
        }
        int iRows = query.numRowsAffected();
    }

    //增加
    if (addList.size()) {
        QVariantList idList;
        QVariantList userIdList;
        for each (int id in addList) {
            userIdList << roleId;
            idList << id;

            QSqlQuery query;
            query.prepare("insert into role_permission(role_id, permission_id) values(:role_id,:permission_id)");
            query.bindValue(":role_id", roleId);
            query.bindValue(":permission_id", id);

            if (!query.exec()) {
                QSqlDatabase::database().rollback();

                MqDebug() << query.lastError().databaseText() << query.lastError().text();
                MqDebug() << query.lastQuery();
                return false;
            }
        }
    }

    QSqlDatabase::database().commit();

    return true;
}

bool CSqliteAuthorityManager::Init()
{
    if (QSqlDatabase::contains("qt_sql_default_connection")) {
        m_db = QSqlDatabase::database("qt_sql_default_connection");
    } else {
        m_db = QSqlDatabase::addDatabase("QSQLITE");


        QString dataPath = CMakePath::make_data_path("permission.db");
        m_db.setDatabaseName(dataPath);
    }

    QString info;
    if (!m_db.open()) {
        info = "error: failed to connect sqlite3 database." + m_db.lastError().text();
		
		m_LastError = ErrCode::kInitFailed;

        return false;
    } else {
        info = "success to connect sqlite3 database.";
        m_ModelUser = new QSqlTableModel(nullptr, m_db);
        //m_ModelUser.database() = m_db;
        m_ModelUser->setTable("users");

        m_ModelRole = new QSqlTableModel(nullptr, m_db);
        m_ModelRole->setTable("roles");

        m_ModelPermission = new QSqlTableModel(nullptr, m_db);
        m_ModelPermission->setTable("permissions");
        return true;
    }
}

bool CSqliteAuthorityManager::CheckExistUser(std::string loginName, bool& bExist)
{
    bExist = true;
    QSqlQuery query;
    query.prepare("select count(1) from users where name =:loginName");
    query.bindValue(":loginName", loginName.c_str());
    if (!query.exec()) {
		m_LastError = ErrCode::kInternalErr;
        return false;
    }

    if (query.next()) {
        int count = query.value(0).toInt();
        if (count == 0) {
            bExist = false;
        }
    }

    return true;
}

bool CSqliteAuthorityManager::CheckExistRole(std::string roleName, bool& bExist)
{
    bExist = true;
    QSqlQuery query;
    query.prepare("select count(1) from roles where name =:roleName");
    query.bindValue(":roleName", roleName.c_str());
    if (!query.exec()) {
		m_LastError = ErrCode::kInternalErr;
        return false;
    }

    if (query.next()) {
        int count = query.value(0).toInt();
        if (count == 0) {
            bExist = false;
        }
    }

    return true;
}

bool CSqliteAuthorityManager::CheckExistPermission(std::string tagName, bool& bExist)
{
	bExist = true;
	QSqlQuery query;
	query.prepare("select count(1) from permissions where name =:tagName");
	query.bindValue(":tagName", tagName.c_str());
	if (!query.exec())
	{
		m_LastError = ErrCode::kInternalErr;
		return false;
	}

	if (query.next())
	{
		int count = query.value(0).toInt();
		if (count == 0)
		{
			bExist = false;
		}
	}

	return true;
}
