/****************************************************************************
 * This file is part of the project AqHome.
 * AqHome (c) by 2025 Martin Preuss, all rights reserved.
 *
 * The license for this file can be found in the file COPYING which you
 * should have received along with this file.
 ****************************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

//#define DISABLE_DEBUGLOG


#include "./service_file_p.h"

#include <gwenhywfar/misc.h>
#include <gwenhywfar/debug.h>



/* ------------------------------------------------------------------------------------------------
 * defs and enums
 * ------------------------------------------------------------------------------------------------
 */

#define AQH_SERVICE_FILE_GROUP_USERS    "users"
#define AQH_SERVICE_FILE_GROUP_MODULES  "modules"
#define AQH_SERVICE_FILE_GROUP_SESSIONS "sessions"


/* ------------------------------------------------------------------------------------------------
 * global vars
 * ------------------------------------------------------------------------------------------------
 */

GWEN_INHERIT(AQH_SERVICE, AQH_SERVICE_FILE)



/* ------------------------------------------------------------------------------------------------
 * forward declarations
 * ------------------------------------------------------------------------------------------------
 */

static void GWENHYWFAR_CB _freeData(void *bp, void *p);

static AQH_USER *_loadUser(AQH_SERVICE *sv, const char *s);
static int _saveUser(AQH_SERVICE *sv, AQH_USER *user);
static int _addUser(AQH_SERVICE *sv, AQH_USER *user);
static int _delUser(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listUsers(AQH_SERVICE *sv);

static AQH_MODULE *_loadModule(AQH_SERVICE *sv, const char *s);
static int _saveModule(AQH_SERVICE *sv, AQH_MODULE *module);
static int _addModule(AQH_SERVICE *sv, AQH_MODULE *module);
static int _delModule(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listModules(AQH_SERVICE *sv);

static AQH_SESSION* _loadSession(AQH_SERVICE *sv, const char *s);
static int _saveSession(AQH_SERVICE *sv, AQH_SESSION *session);
static int _addSession(AQH_SERVICE *sv, AQH_SESSION *session);
static int _delSession(AQH_SERVICE *sv, const char *s);
static GWEN_STRINGLIST *_listSessions(AQH_SERVICE *sv);

static GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName);
static int _saveGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
static int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);
static int _deleteGroup(AQH_SERVICE *sv, const char *groupName, const char *subGroupName);
static GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName);

//static void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db);



/* ------------------------------------------------------------------------------------------------
 * code
 * ------------------------------------------------------------------------------------------------
 */

AQH_SERVICE *AQH_ServiceFiles_new(const char *baseFolder, const char *baseUrl)
{
  AQH_SERVICE *sv;
  AQH_SERVICE_FILE *xs;
  GWEN_BUFFER *dbuf;

  sv=AQH_Service_new(baseFolder, baseUrl);
  GWEN_NEW_OBJECT(AQH_SERVICE_FILE, xs);
  GWEN_INHERIT_SETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv, xs, _freeData);

  AQH_Service_SetLoadUserFn(sv, _loadUser);
  AQH_Service_SetSaveUserFn(sv, _saveUser);
  AQH_Service_SetAddUserFn(sv, _addUser);
  AQH_Service_SetDelUserFn(sv, _delUser);
  AQH_Service_SetListUsersFn(sv, _listUsers);

  AQH_Service_SetLoadModuleFn(sv, _loadModule);
  AQH_Service_SetSaveModuleFn(sv, _saveModule);
  AQH_Service_SetAddModuleFn(sv, _addModule);
  AQH_Service_SetDelModuleFn(sv, _delModule);
  AQH_Service_SetListModulesFn(sv, _listModules);

  AQH_Service_SetLoadSessionFn(sv, _loadSession);
  AQH_Service_SetSaveSessionFn(sv, _saveSession);
  AQH_Service_SetAddSessionFn(sv, _addSession);
  AQH_Service_SetDelSessionFn(sv, _delSession);
  AQH_Service_SetListSessionsFn(sv, _listSessions);

  dbuf=GWEN_Buffer_new(0, 256, 0, 1);
  GWEN_Buffer_AppendArgs(dbuf, "dir://%s", AQH_Service_GetRuntimeFolder(sv));
  DBG_ERROR(NULL, "Creating config mgr \"%s\"", GWEN_Buffer_GetStart(dbuf));
  xs->configMgr=GWEN_ConfigMgr_Factory(GWEN_Buffer_GetStart(dbuf));
  if (xs->configMgr==NULL) {
    DBG_ERROR(NULL, "Error creating config mgr \"%s\"", GWEN_Buffer_GetStart(dbuf));
    GWEN_Buffer_free(dbuf);
    AQH_Service_free(sv);
    return NULL;
  }
  GWEN_Buffer_free(dbuf);

  return sv;
}



void GWENHYWFAR_CB _freeData(GWEN_UNUSED void *bp, void *p)
{
  AQH_SERVICE_FILE *xs;

  xs=(AQH_SERVICE_FILE*) p;
  GWEN_ConfigMgr_free(xs->configMgr);
  GWEN_FREE_OBJECT(xs);
}



AQH_USER *_loadUser(AQH_SERVICE *sv, const char *s)
{
  GWEN_DB_NODE *db;

  db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s);
  if (db) {
    AQH_USER *user;

    user=AQH_User_fromDb(db);
    GWEN_DB_Group_free(db);
    return user;
  }

  return NULL;
}



int _saveUser(AQH_SERVICE *sv, AQH_USER *user)
{
  const char *s;

  s=AQH_User_GetAlias(user);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_User_toDb(user, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }

    //_logGroup(AQH_SERVICE_FILE_GROUP_USERS, s, db);

    rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  return GWEN_ERROR_INVALID;
}



int _addUser(AQH_SERVICE *sv, AQH_USER *user)
{
  const char *s;

  s=AQH_User_GetAlias(user);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_User_toDb(user, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_USERS, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  else {
    DBG_ERROR(NULL, "Missing alias");
  }
  return GWEN_ERROR_INVALID;
}



int _delUser(AQH_SERVICE *sv, const char *s)
{
  return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_USERS, s);
}



GWEN_STRINGLIST *_listUsers(AQH_SERVICE *sv)
{
  return _listGroup(sv, AQH_SERVICE_FILE_GROUP_USERS);
}



AQH_MODULE *_loadModule(AQH_SERVICE *sv, const char *s)
{
  GWEN_DB_NODE *db;

  db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s);
  if (db) {
    AQH_MODULE *module;

    module=AQH_Module_fromDb(db);
    GWEN_DB_Group_free(db);
    return module;
  }

  return NULL;
}



int _saveModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
  const char *s;

  s=AQH_Module_GetName(module);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_Module_toDb(module, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  return GWEN_ERROR_INVALID;
}



int _addModule(AQH_SERVICE *sv, AQH_MODULE *module)
{
  const char *s;

  s=AQH_Module_GetName(module);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_Module_toDb(module, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_MODULES, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  return GWEN_ERROR_INVALID;
}



int _delModule(AQH_SERVICE *sv, const char *s)
{
  return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_MODULES, s);
}



GWEN_STRINGLIST *_listModules(AQH_SERVICE *sv)
{
  return _listGroup(sv, AQH_SERVICE_FILE_GROUP_MODULES);
}




AQH_SESSION* _loadSession(AQH_SERVICE *sv, const char *s)
{
  GWEN_DB_NODE *db;

  db=_loadGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s);
  if (db) {
    AQH_SESSION *session;

    session=AQH_Session_fromDb(db);
    GWEN_DB_Group_free(db);
    return session;
  }

  return NULL;
}



int _saveSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
  const char *s;

  s=AQH_Session_GetUid(session);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_Session_toDb(session, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    rv=_saveGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  return GWEN_ERROR_INVALID;
}



int _addSession(AQH_SERVICE *sv, AQH_SESSION *session)
{
  const char *s;

  s=AQH_Session_GetUid(session);
  if (s && *s) {
    GWEN_DB_NODE *db;
    int rv;

    db=GWEN_DB_Group_new(s);
    rv=AQH_Session_toDb(session, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    rv=_addGroupLocked(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s, db);
    if (rv<0) {
      DBG_ERROR(NULL, "here (%d)", rv);
      GWEN_DB_Group_free(db);
      return rv;
    }
    GWEN_DB_Group_free(db);
    return 0;
  }
  return GWEN_ERROR_INVALID;
}



int _delSession(AQH_SERVICE *sv, const char *s)
{
  return _deleteGroup(sv, AQH_SERVICE_FILE_GROUP_SESSIONS, s);
}



GWEN_STRINGLIST *_listSessions(AQH_SERVICE *sv)
{
  return _listGroup(sv, AQH_SERVICE_FILE_GROUP_SESSIONS);
}



GWEN_DB_NODE *_loadGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName)
{
  DBG_ERROR(NULL, "Lock and load group %s/%s", groupName, subGroupName);
  if (sv && groupName && subGroupName) {
    AQH_SERVICE_FILE *xs;

    xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
    if (xs) {
      GWEN_DB_NODE *db=NULL;
      int rv;

      DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
	return NULL;
      }
      DBG_ERROR(NULL, "Loading group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_GetGroup(xs->configMgr, groupName, subGroupName, &db);
      if (rv<0) {
	DBG_ERROR(NULL, "Error reading group \"%s/%s\": %d", groupName, subGroupName, rv);
	GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
	return NULL;
      }
      DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
	GWEN_DB_Group_free(db);
	return NULL;
      }
      return db;
    }
  }
  else {
    DBG_ERROR(NULL, "Missing argument");
  }
  return NULL;
}



int _saveGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
  if (sv && groupName && subGroupName && db) {
    AQH_SERVICE_FILE *xs;

    xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
    if (xs) {
      int rv;

      DBG_ERROR(NULL, "Locking group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      DBG_ERROR(NULL, "Writing group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_SetGroup(xs->configMgr, groupName, subGroupName, db);
      if (rv<0) {
	DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      DBG_ERROR(NULL, "Unlocking group %s/%s", groupName, subGroupName);
      rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      return 0;
    }
  }
  return GWEN_ERROR_INVALID;
}



int _addGroupLocked(AQH_SERVICE *sv, const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
  if (sv && groupName && subGroupName && db) {
    AQH_SERVICE_FILE *xs;

    xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
    if (xs) {
      int rv;

      rv=GWEN_ConfigMgr_HasGroup(xs->configMgr, groupName, subGroupName);
      if (rv>=0) {
	DBG_ERROR(NULL, "Group \"%s/%s\" already exists", groupName, subGroupName);
	return GWEN_ERROR_FOUND;
      }

      rv=GWEN_ConfigMgr_LockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error locking group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      rv=GWEN_ConfigMgr_SetGroup(xs->configMgr, groupName, subGroupName, db);
      if (rv<0) {
	DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      rv=GWEN_ConfigMgr_UnlockGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error unlocking group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }

      return 0;
    }
  }
  return GWEN_ERROR_INVALID;
}



int _deleteGroup(AQH_SERVICE *sv, const char *groupName, const char *subGroupName)
{
  if (sv && groupName && subGroupName) {
    AQH_SERVICE_FILE *xs;

    xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
    if (xs) {
      int rv;

      rv=GWEN_ConfigMgr_DeleteGroup(xs->configMgr, groupName, subGroupName);
      if (rv<0) {
	DBG_ERROR(NULL, "Error writing group \"%s/%s\": %d", groupName, subGroupName, rv);
	return rv;
      }
      return 0;
    }
  }
  return GWEN_ERROR_INVALID;
}



GWEN_STRINGLIST *_listGroup(AQH_SERVICE *sv, const char *groupName)
{
  if (sv && groupName) {
    AQH_SERVICE_FILE *xs;

    xs=GWEN_INHERIT_GETDATA(AQH_SERVICE, AQH_SERVICE_FILE, sv);
    if (xs) {
      GWEN_STRINGLIST *sl;
      int rv;

      sl=GWEN_StringList_new();
      rv=GWEN_ConfigMgr_ListSubGroups(xs->configMgr, groupName, sl);
      if (rv<0) {
	DBG_ERROR(NULL, "Error listing group \"%s\": %d", groupName, rv);
	GWEN_StringList_free(sl);
	return NULL;
      }
      if (GWEN_StringList_Count(sl)==0) {
	GWEN_StringList_free(sl);
	return NULL;
      }
      return sl;
    }
  }
  return NULL;
}



#if 0
void _logGroup(const char *groupName, const char *subGroupName, GWEN_DB_NODE *db)
{
  if (db) {
    GWEN_BUFFER *dbuf;

    dbuf=GWEN_Buffer_new(0, 256, 0, 1);
    GWEN_DB_WriteToBuffer(db, dbuf, GWEN_DB_FLAGS_DEFAULT);
    DBG_ERROR(NULL, "Group %s/%s:\n%s", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>",
              GWEN_Buffer_GetStart(dbuf));
    GWEN_Buffer_free(dbuf);
  }
  else {
    DBG_ERROR(NULL, "Group %s/%s empty", groupName?groupName:"<empty>", subGroupName?subGroupName:"<empty>");
  }
}
#endif



