using System; using System.Threading.Tasks; using FrameworkBLL.GOP; using GB5Shared.DTO.Framework.Login; using GB5Shared.DTO.Framework.ResponseStandard; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; using static GB5Shared.GB5Constant.Constant; namespace FrameworkSL.Controllers.GOP { /// /// GOP Document Intake Controller — the generic "AI identifies the document, GOP picks the /// flow" pipeline (see IGopDocumentIntakeBLL's doc comment). Recruitment's resume-to-Candidate /// conversion is its first consumer, not a special case baked into it. /// /// POST SubmitDocument — accepts any file, classifies it via EAIAdmin's IDENTIFY_DOCUMENT /// capability, resolves the matching MGOPDOCUMENTTYPE, converts it via /// PARSE_VALIDATE_DOCUMENT, and submits the result as a normal GOP /// execution (IGopQueueBLL.SubmitExecution, unchanged). Returns /// immediately with an ExecutionId to poll/subscribe on, same as /// GopQueueService/SubmitExecution. /// [ApiController] [Route("[controller]")] public class GopDocumentIntakeService : ControllerBase { private readonly IGopDocumentIntakeBLL _IGopDocumentIntakeBLL; public GopDocumentIntakeService(IGopDocumentIntakeBLL gopDocumentIntakeBLL) { _IGopDocumentIntakeBLL = gopDocumentIntakeBLL; } [HttpPost("SubmitDocument")] public async Task> SubmitDocument( [FromForm] GopDocumentIntakeRequestDTO request, [FromHeader(Name = "Login")] string LoginDTOJson) { var LoginDTO = JsonConvert.DeserializeObject(LoginDTOJson); try { if (request.File is null || request.File.Length == 0) throw new ArgumentException("No file provided or file is empty."); await using var stream = request.File.OpenReadStream(); var result = await _IGopDocumentIntakeBLL.SubmitDocumentAsync( stream, request.File.FileName, request.File.ContentType, LoginDTO!) .ConfigureAwait(false); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( result, CacheKeyLevel.NOT_REQUIRED, LoginDTO!); } catch (Exception ex) { return await GB5Shared.ResponseStandard.Response.CreateExceptionError( ex, CacheKeyLevel.NOT_REQUIRED, LoginDTO!, ex.Message, 500); } } /// Human-in-the-loop variant — see IGopDocumentIntakeBLL.SubmitDocumentForPrefillAsync. /// The caller supplies SourceCode directly (e.g. "RESUME_PREFILL") instead of relying on /// AI classification; the resolved flow has no save step, so nothing is persisted until /// the caller's own form is reviewed and saved by the user. [HttpPost("PrefillFromDocument")] public async Task> PrefillFromDocument( [FromForm] GopDocumentIntakePrefillRequestDTO request, [FromHeader(Name = "Login")] string LoginDTOJson) { var LoginDTO = JsonConvert.DeserializeObject(LoginDTOJson); try { if (request.File is null || request.File.Length == 0) throw new ArgumentException("No file provided or file is empty."); await using var stream = request.File.OpenReadStream(); var result = await _IGopDocumentIntakeBLL.SubmitDocumentForPrefillAsync( stream, request.File.FileName, request.File.ContentType, request.SourceCode, LoginDTO!) .ConfigureAwait(false); return await GB5Shared.ResponseStandard.Response.CreateSuccessResponse( result, CacheKeyLevel.NOT_REQUIRED, LoginDTO!); } catch (Exception ex) { return await GB5Shared.ResponseStandard.Response.CreateExceptionError( ex, CacheKeyLevel.NOT_REQUIRED, LoginDTO!, ex.Message, 500); } } } public class GopDocumentIntakeRequestDTO { public IFormFile? File { get; set; } } public class GopDocumentIntakePrefillRequestDTO { public IFormFile? File { get; set; } public string SourceCode { get; set; } = string.Empty; } }