using EntitlementDAL.DTOs;
namespace EntitlementBLL.Demo;
/// Thread 3 (Demo/DemoDB, tracker §49) — registration, checkout/extension/expiry
/// lifecycle for both Pooled and Dedicated demo sessions.
public interface IDemoSessionBLL
{
/// Decision 9 — the shared, lightweight first step for EVERY demo request (Pooled
/// or Dedicated alike): creates a real central MCLIENT/MUSER account (reusing
/// IClientProvisioningBLL.CreateClientAsync — identity only, no DB provisioning, so this is
/// fast enough for a public landing-page form) and captures the prospect's industry/product/
/// plan/feature preference. The returned ClientId is what CheckOutPooledSessionRequest/
/// StartDedicatedSessionRequest both require.
Task RegisterProspectAsync(RegisterDemoProspectRequest req, CancellationToken ct);
Task CheckOutPooledSessionAsync(CheckOutPooledSessionRequest req, CancellationToken ct);
/// Reuses IClientOnboardingOrchestratorBLL.StartOnboardingAsync, now passing
/// ExistingClientId/ExistingUserId (from the prior RegisterProspectAsync call) so it
/// provisions a real physical DB for the SAME central account instead of creating a second,
/// duplicate identity — a Dedicated demo gets a real login for a multi-day evaluation.
Task StartDedicatedSessionAsync(StartDedicatedSessionRequest req, CancellationToken ct);
Task GetByIdAsync(int demoSessionId, CancellationToken ct);
Task> GetListByOwnerAsync(int ownerUserId, CancellationToken ct);
Task RequestExtensionAsync(int demoSessionId, string? comment, CancellationToken ct);
/// Only the session's own OwnerUserId (the sales/CS rep who owns the engagement) may
/// approve — per the user's own confirmed choice, tracker §49 Decision 5.
Task ApproveExtensionAsync(int demoSessionId, int approvedById, DateTime newExpiresOn, CancellationToken ct);
Task RejectExtensionAsync(int demoSessionId, int rejectedById, CancellationToken ct);
/// Background worker entry point (mirrors RolloutCampaignWorkerJob's own shape) —
/// issues warnings for sessions approaching expiry, expires sessions past expiry, and (Pooled
/// only) resets the underlying pool instance for reuse. Returns the number of sessions
/// touched (warned + expired) this pass.
Task ProcessExpiringAndExpiredSessionsAsync(CancellationToken ct);
}
/// Decision 9's simple, landing-page-driven registration form — deliberately no
/// ClientCode/AdminUserCode (server-derived, same discipline as
/// ClientOnboardingOrchestratorBLL.StartSelfServiceTrialAsync, so an anonymous caller can't
/// spoof/collide with another client's code).
public class RegisterDemoProspectRequest
{
public string CompanyName { get; set; } = string.Empty;
public string ContactName { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string Mobile { get; set; } = string.Empty;
/// Soft reference, no hard FK — TPARTNERPRODUCT.PartnerProductId; null = GB5 default.
public int? PreferredPartnerProductId { get; set; }
public int? PreferredPlanId { get; set; }
public string PreferredIndustryCode { get; set; } = string.Empty;
public string PreferredGeographyCode { get; set; } = string.Empty;
public int? PreferredFeatureId { get; set; }
// Legal/Contract Agreement Consent jurisdiction resolution (tracker §51.12) — deliberately
// distinct from PreferredGeographyCode above: that's content-selection taxonomy for picking a
// demo variant (e.g. "Africa"/"FarEast"), not a legal jurisdiction code. Optional.
public string? JurisdictionCode { get; set; }
// Legal/Contract Agreement Consent (tracker §50/§51) — mirrors
// SelfProvisionTrialRequestDTO.AcceptedAgreementVersionIds exactly: the exact
// AgreementVersionIds the caller was shown (via Legal/GetApplicableAgreements) and ticked
// to accept. Optional/empty is tolerated (no acceptance recorded, nothing blocked).
public int[] AcceptedAgreementVersionIds { get; set; } = Array.Empty();
}
/// The one place the first admin's plaintext temporary password is visible — same
/// convention as CreateClientResultDTO/SelfProvisionTrialResultDTO.
public class RegisterDemoProspectResultDTO
{
public int ClientId { get; set; }
public int UserId { get; set; }
public string TemporaryPassword { get; set; } = string.Empty;
}
public class CheckOutPooledSessionRequest
{
/// From a prior RegisterProspectAsync call — the central account this session
/// belongs to (Decision 9: required for Pooled mode too, not just Dedicated).
public int ClientId { get; set; }
/// From the same prior RegisterProspectAsync call as ClientId — needed to seed the
/// real per-prospect MUSER inside the pool instance's own database with the same Name/Email
/// already captured centrally, rather than asking for it a second time.
public int UserId { get; set; }
public int DemoVariantId { get; set; }
public int OwnerUserId { get; set; }
public int DurationHours { get; set; } = 24;
/// Optional — the role the prospect wants to see (e.g. "TMS_TRAINER"), matched
/// against MDEMOVARIANT.AvailableDemoRoleCodes. Stored on the session for reporting; the
/// actual per-prospect MUSER-inside-the-pool-DB creation this drives is designed (Decision
/// 8/9) but not yet built — see tracker §49's own drift notes.
public string? DemoRoleCode { get; set; }
/// Direction B (a CRM sales exec pushing a demo to an existing lead) — set directly;
/// left null for Direction A (self-service).
public int? LeadId { get; set; }
}
public class StartDedicatedSessionRequest
{
/// From a prior RegisterProspectAsync call — reused, never a second identity for
/// the same prospect (Decision 9). Threaded onto OnboardRequest.ExistingClientId/UserId.
public int ClientId { get; set; }
public int UserId { get; set; }
public OnboardClientRequestDTO OnboardRequest { get; set; } = new();
public int OwnerUserId { get; set; }
public int DurationDays { get; set; } = 14;
public int? LeadId { get; set; }
}