using GB5Shared.DTO.Framework.Login;
using RecruitmentDAL.DTO.Application;
using RecruitmentDAL.DTO.Interview;
using RecruitmentDAL.DTO.JobOffer;
using RecruitmentDAL.DTO.JobRequisition;
namespace RecruitmentBLL.Integration
{
/// Outcome of a call that creates/mutates something in ECP — modeled after
/// DXPBLL.VendorPo.MmIntegrationService's Outcome pattern. Never throws out to the caller;
/// a failed ECP call degrades to Success=false with an Error, so an ECP outage never breaks
/// an Interview/Application save (see EcpIntegrationService's class doc comment).
public class EcpCallOutcome
{
public bool Success { get; set; }
public string? Error { get; set; }
}
/// Outcome of ScheduleMeetingAsync — carries the ECP-side MeetingId back so the
/// caller can persist it onto TINTERVIEW.ECPMEETINGID.
public class EcpMeetingScheduleOutcome : EcpCallOutcome
{
public int MeetingId { get; set; } = -1;
}
///
/// Cross-host HTTP integration with ECP (hosted by EngagementHost, a different process than
/// Recruitment's HRFinanceHost) — no ProjectReference/DI injection of ECP's BLL exists or is
/// possible across hosts (confirmed: no example anywhere in this repo of one module directly
/// injecting another host's BLL), so every call here is a named-HttpClient POST/GET against
/// ECP's verified FastEndpoints routes, following the exact
/// IHttpClientFactory + "Login" header pattern used by
/// DXPBLL.VendorPo.MmIntegrationService / PAYBLL.Integration.FinanceIntegrationService.
///
/// Every method here degrades gracefully: a non-2xx response or thrown exception (timeout,
/// DNS failure, ECP process down) is logged and returned as a failure/null result — it never
/// throws back into InterviewBLL/ApplicationBLL, so an ECP outage never blocks an
/// Interview/Application save.
///
public interface IEcpIntegrationService
{
/// POSTs /Meeting/SaveMeeting for the interview, then POSTs
/// /MeetingDetails/AddMeetingParticipant for the lead InterviewerId only — panel-member
/// fan-out (TINTERVIEWPANEL) is a fast-follow once that entity's schema/BLL lands from a
/// parallel task, not implemented here.
Task ScheduleMeetingAsync(InterviewDTO interview, LoginDTO login, CancellationToken ct);
/// POSTs /MeetingRecording/AttachRecording — the caller must have already
/// uploaded the recording as a TATTACHMENT and pass its id.
Task AttachInterviewRecordingAsync(int interviewId, int recordingAttachmentId, LoginDTO login, CancellationToken ct);
/// GETs /MeetingRecording/GetRecordingTranscriptsForObject. Returns the raw JSON
/// array (ECP's ResponseStandardDTO.Body content) so Recruitment never needs a
/// ProjectReference to ECPDAL's RecordingTranscriptDTO; null on failure.
Task GetInterviewRecordingsAsync(int interviewId, LoginDTO login, CancellationToken ct);
/// POSTs /CommentThread/SaveCommentThread against the Application object.
Task PostApplicationCommentAsync(int applicationId, string commentText, List mentionedUserIds, LoginDTO login, CancellationToken ct);
/// GETs /CommentThread/GetCommentThreadTree for the Application object. Returns
/// the raw JSON tree (ECP's ResponseStandardDTO.Body content); null on failure.
Task GetApplicationCommentsAsync(int applicationId, LoginDTO login, CancellationToken ct);
// ── Phase 3: Correspondence (offer/interview/rejection letters) ────────────────────
// All four POST /Correspondence/SaveAndSendCorrespondence with the matching
// RecruitmentCorrespondenceTypes.* CorrespondenceTypeId and a TemplateId read from
// "Integration:CorrespondenceTemplates:*" config (an ECP TATTACHMENT id of a previously
// uploaded .docx/.xlsx template — see SaveAndSendCorrespondence's real request shape).
// If that TemplateId isn't configured (<=0), or the recipient email is empty, the call
// degrades to Success=false without an HTTP round-trip — same graceful-degradation
// contract as every other method on this interface, extended to cover "not configured yet"
// as well as "ECP unreachable".
/// Sends the offer letter for a newly-saved JobOffer. Merge fields are built from
/// JobOfferDTO's own columns (CTC, ExpectedJoiningDate, OfferExpiryDate, JobPositionId as
/// Position) — no cross-module JobPosition-title lookup is performed.
Task SendOfferLetterAsync(JobOfferDTO jobOffer, string recipientEmail, LoginDTO login, CancellationToken ct);
/// Sends an interview scheduling notification for a newly-saved or rescheduled
/// Interview.
Task SendInterviewInviteAsync(InterviewDTO interview, string recipientEmail, LoginDTO login, CancellationToken ct);
/// Sends a candidate rejection letter for an Application.
Task SendRejectionLetterAsync(ApplicationDTO application, string recipientEmail, LoginDTO login, CancellationToken ct);
/// Notifies the original JobRequisition requester (RequestedById, resolved to an
/// email via MUSER.PRIMARYMAIL — same column CRMDAL/ECPDAL's own MUSER lookups already use)
/// that their hire has joined — closes the loop back to whoever asked for the position.
/// Degrades gracefully (Success=false) if RequestedById has no resolvable email.
Task NotifyRequesterOnHireAsync(JobRequisitionDTO jobRequisition, string hiredCandidateName, LoginDTO login, CancellationToken ct);
}
}