using System.ComponentModel.DataAnnotations;
namespace GerbilManagerWebAPI.Models
{
/// Triage state of an incoming Gmail request (serialised as string name).
public enum RequestStatus
{
New = 0,
InProgress = 1,
Assigned = 2,
Answered = 3,
Abandoned = 4,
}
///
/// An incoming inquiry imported from Gmail (INBOX epic). One row per email,
/// deduped on the RFC Message-Id.
///
public class Request
{
[Key]
public Guid Id { get; set; }
/// RFC 5322 Message-Id — unique; the dedup key.
public required string GmailMessageId { get; set; }
/// Gmail conversation id (X-GM-THRID).
public string? ThreadId { get; set; }
public string? InReplyToMessageId { get; set; }
public string? ReferencesHeader { get; set; }
public required string FromAddress { get; set; }
public string? FromName { get; set; }
public string? Subject { get; set; }
public string? BodyText { get; set; }
public DateTimeOffset ReceivedAt { get; set; }
public RequestStatus Status { get; set; } = RequestStatus.New;
public Guid? AssignedContactId { get; set; }
public Contact? AssignedContact { get; set; }
/// AI/edited draft reply (INBOX-2); send is INBOX-3. Null until drafted.
public string? DraftReply { get; set; }
public DateTimeOffset? AnsweredAt { get; set; }
}
///
/// Singleton mail configuration (INBOX epic). The Gmail App Password is stored
/// ENCRYPTED at rest (ASP.NET Data Protection) and never returned over the wire.
///
public class MailSettings
{
public static readonly Guid SingletonId = new("ab0c0000-0000-0000-0000-000000000001");
[Key]
public Guid Id { get; set; }
public string? GmailAddress { get; set; }
/// Data-Protection-encrypted Gmail App Password (never plaintext, never on the wire).
public string? AppPasswordProtected { get; set; }
public int PollIntervalMinutes { get; set; } = 15;
public string Folder { get; set; } = "INBOX";
public bool BackgroundPollEnabled { get; set; }
/// Highest IMAP UID seen, to avoid rescanning the whole folder.
public uint LastUid { get; set; }
}
}