using LiteDB;
namespace Campus_live_rewrite.api.dbs
{
///
/// Website support tickets. A signed-in player opens a ticket (from the Support page bot's
/// "talk to a real person" flow); staff (dev / mod / owner) see EVERY ticket and can reply,
/// close and reopen. Stored in its own LiteDB file; checkpointed by DbMaintenance.
///
public static class SupportDB
{
private static readonly LiteDatabase DB =
new LiteDatabase(Environment.CurrentDirectory + "/Data/DBs/Support.db");
public static void Checkpoint() => DB.Checkpoint();
private static ILiteCollection Tickets => DB.GetCollection("tickets");
static SupportDB()
{
Tickets.EnsureIndex(x => x.AccountId);
Tickets.EnsureIndex(x => x.Status);
}
public sealed class TicketMessage
{
public ulong FromAccountId { get; set; }
public string FromName { get; set; } = "";
public bool Staff { get; set; }
public string Body { get; set; } = "";
public DateTime At { get; set; } = DateTime.UtcNow;
}
public sealed class Ticket
{
public int Id { get; set; } // LiteDB auto-increment PK
public ulong AccountId { get; set; }
public string Username { get; set; } = "";
public string Subject { get; set; } = "";
public string Status { get; set; } = "open"; // "open" | "closed"
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public List Messages { get; set; } = new();
}
public static Ticket Create(ulong accountId, string username, string subject, string body)
{
var t = new Ticket
{
AccountId = accountId,
Username = username,
Subject = string.IsNullOrWhiteSpace(subject) ? "Support request" : subject,
Status = "open",
};
t.Messages.Add(new TicketMessage { FromAccountId = accountId, FromName = username, Staff = false, Body = body });
Tickets.Insert(t); // Id auto-assigned
return t;
}
public static Ticket? Get(int id) => Tickets.FindById(id);
public static List ForAccount(ulong accountId) =>
Tickets.Find(x => x.AccountId == accountId).OrderByDescending(x => x.UpdatedAt).ToList();
public static List All(string? status)
{
var rows = status == "open" ? Tickets.Find(x => x.Status == "open")
: status == "closed" ? Tickets.Find(x => x.Status == "closed")
: Tickets.FindAll();
return rows.OrderByDescending(x => x.UpdatedAt).ToList();
}
public static bool AddMessage(int id, ulong fromId, string fromName, bool staff, string body)
{
var t = Tickets.FindById(id);
if (t == null) return false;
t.Messages.Add(new TicketMessage { FromAccountId = fromId, FromName = fromName, Staff = staff, Body = body });
t.UpdatedAt = DateTime.UtcNow;
// A reply on a closed ticket reopens it, so nobody's follow-up is lost in a closed thread.
if (t.Status == "closed") t.Status = "open";
Tickets.Update(t);
return true;
}
public static bool SetStatus(int id, string status)
{
var t = Tickets.FindById(id);
if (t == null) return false;
t.Status = status;
t.UpdatedAt = DateTime.UtcNow;
Tickets.Update(t);
return true;
}
public static int OpenCount() => Tickets.Count(x => x.Status == "open");
}
}