using System.Security.Cryptography; using System.Text; using EntitlementDAL.DTOs; using EntitlementDAL.Interfaces; using GB5Shared.DTO.Framework.Login; using GB5Shared.GenerateAutoNumber; using static GB5Shared.GB5Constant.Constant; namespace EntitlementBLL.Legal; public class AgreementAuthoringBLL : IAgreementAuthoringBLL { private readonly IAgreementDAL _AgreementDAL; private readonly AutoNumber _AutoNumber; public AgreementAuthoringBLL(IAgreementDAL agreementDAL, AutoNumber autoNumber) { _AgreementDAL = agreementDAL; _AutoNumber = autoNumber; } public async Task EnsureTypeAsync(string typeCode, string typeName, bool requiresIndividualAcceptance, LoginDTO login, CancellationToken ct) { if (string.IsNullOrWhiteSpace(typeCode)) throw new ArgumentException("TypeCode is required.", nameof(typeCode)); if (string.IsNullOrWhiteSpace(typeName)) throw new ArgumentException("TypeName is required.", nameof(typeName)); var existing = await _AgreementDAL.GetTypeByCodeAsync(typeCode, login, ct).ConfigureAwait(false); if (existing is not null) return existing; var auto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.ENTITLEMENTAGREEMENTTYPE, login).ConfigureAwait(false); var now = DateTime.UtcNow; var dto = new AgreementTypeDTO { AgreementTypeId = auto.StartNumber, TypeCode = typeCode, TypeName = typeName, RequiresIndividualAcceptance = requiresIndividualAcceptance, Version = 0, Status = 1, CreatedById = login.UserId, CreatedOn = now, ModifiedById = login.UserId, ModifiedOn = now, SourceType = 0, }; await _AgreementDAL.InsertTypeAsync(dto, login, ct).ConfigureAwait(false); return dto; } public async Task> GetTypeListAsync(LoginDTO login, CancellationToken ct) => await _AgreementDAL.GetTypeListAsync(login, ct).ConfigureAwait(false); public async Task> GetVersionListByTypeAsync(int agreementTypeId, LoginDTO login, CancellationToken ct) => await _AgreementDAL.GetVersionListByTypeAsync(agreementTypeId, login, ct).ConfigureAwait(false); public async Task CreateDraftVersionAsync( string typeCode, string versionLabel, string? jurisdictionCode, string? contentRef, DateTime effectiveFrom, LoginDTO login, CancellationToken ct) { if (string.IsNullOrWhiteSpace(versionLabel)) throw new ArgumentException("VersionLabel is required.", nameof(versionLabel)); var type = await _AgreementDAL.GetTypeByCodeAsync(typeCode, login, ct).ConfigureAwait(false) ?? throw new InvalidOperationException($"AgreementType '{typeCode}' does not exist — call EnsureTypeAsync first."); var auto = await _AutoNumber.GetNumberAsync(1, AUTONUMBERCONSTANT.ENTITLEMENTAGREEMENTVERSION, login).ConfigureAwait(false); var now = DateTime.UtcNow; var dto = new AgreementVersionDTO { AgreementVersionId = auto.StartNumber, AgreementTypeId = type.AgreementTypeId, VersionLabel = versionLabel, JurisdictionCode = jurisdictionCode, LanguageId = -1, ContentRef = contentRef, ContentHash = null, EffectiveFrom = effectiveFrom, VersionStatus = (byte)AgreementVersionStatus.Draft, Version = 0, Status = 1, CreatedById = login.UserId, CreatedOn = now, ModifiedById = login.UserId, ModifiedOn = now, SourceType = 0, }; await _AgreementDAL.InsertVersionAsync(dto, login, ct).ConfigureAwait(false); return dto; } public async Task PublishVersionAsync(int agreementVersionId, LoginDTO login, CancellationToken ct) { var version = await _AgreementDAL.GetVersionByIdAsync(agreementVersionId, login, ct).ConfigureAwait(false) ?? throw new InvalidOperationException($"AgreementVersionId {agreementVersionId} not found."); if (version.VersionStatus != (byte)AgreementVersionStatus.Draft) throw new InvalidOperationException($"AgreementVersionId {agreementVersionId} is not Draft (current status: {(AgreementVersionStatus)version.VersionStatus}) — only a Draft version can be Published."); // ContentHash computed at publish time, not draft time — ContentRef may still change // while Draft (this interface's own doc comment on PublishVersionAsync). var contentHash = string.IsNullOrEmpty(version.ContentRef) ? null : Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(version.ContentRef))).ToLowerInvariant(); await _AgreementDAL.PublishVersionAsync(agreementVersionId, contentHash, login.UserId, DateTime.UtcNow, login, ct).ConfigureAwait(false); } public async Task SupersedeVersionAsync(int agreementVersionId, LoginDTO login, CancellationToken ct) { var version = await _AgreementDAL.GetVersionByIdAsync(agreementVersionId, login, ct).ConfigureAwait(false) ?? throw new InvalidOperationException($"AgreementVersionId {agreementVersionId} not found."); if (version.VersionStatus != (byte)AgreementVersionStatus.Published) throw new InvalidOperationException($"AgreementVersionId {agreementVersionId} is not Published (current status: {(AgreementVersionStatus)version.VersionStatus}) — only a Published version can be Superseded."); await _AgreementDAL.UpdateVersionStatusAsync( agreementVersionId, (byte)AgreementVersionStatus.Superseded, login.UserId, DateTime.UtcNow, login, ct).ConfigureAwait(false); } }