using GB5Shared.DTO.Framework.Login; using GB5Shared.Resource.Response; using Microsoft.Extensions.Logging; using TCMSBLL.Interfaces; using TCMSDAL.DTO; using TCMSDAL.Interfaces; namespace TCMSBLL.Implementations; public class TCMSTraceabilityBLL : ITCMSTraceabilityBLL { private readonly ITCMSTraceabilityDAL _Dal; private readonly ILogger _Log; public TCMSTraceabilityBLL( ITCMSTraceabilityDAL dal, ILogger logger) { _Dal = dal; _Log = logger; } // ── Screen links ────────────────────────────────────────────────────────── public async Task LinkScreenAsync( TCMSSaveLinkDTO dto, LoginDTO login, CancellationToken ct) { if (dto.TestCaseId <= 0) return "Test case is required."; if (string.IsNullOrWhiteSpace(dto.ScreenRoute)) return "Screen route is required."; if (string.IsNullOrWhiteSpace(dto.ScreenTitle)) return "Screen title is required."; if (string.IsNullOrWhiteSpace(dto.ModuleName)) return "Module name is required."; var id = await _Dal.InsertScreenLinkAsync(dto, login, ct).ConfigureAwait(false); return $"{SuccessResponse.SaveSuccessMessage} {id}"; } public async Task UnlinkScreenAsync( int screenLinkId, LoginDTO login, CancellationToken ct) { await _Dal.DeleteScreenLinkAsync(screenLinkId, login, ct).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } public Task> GetScreenLinksForTestCaseAsync( int testCaseId, LoginDTO login, CancellationToken ct) => _Dal.GetScreenLinksForTestCaseAsync(testCaseId, login, ct); // ── API links ───────────────────────────────────────────────────────────── public async Task LinkApiAsync( TCMSSaveApiLinkDTO dto, LoginDTO login, CancellationToken ct) { if (dto.TestCaseId <= 0) return "Test case is required."; if (string.IsNullOrWhiteSpace(dto.ApiPath)) return "API path is required."; if (string.IsNullOrWhiteSpace(dto.HttpMethod)) return "HTTP method is required."; var id = await _Dal.InsertApiLinkAsync(dto, login, ct).ConfigureAwait(false); return $"{SuccessResponse.SaveSuccessMessage} {id}"; } public async Task UnlinkApiAsync( int apiLinkId, LoginDTO login, CancellationToken ct) { await _Dal.DeleteApiLinkAsync(apiLinkId, login, ct).ConfigureAwait(false); return SuccessResponse.DeleteSuccessMessage; } public Task> GetApiLinksForTestCaseAsync( int testCaseId, LoginDTO login, CancellationToken ct) => _Dal.GetApiLinksForTestCaseAsync(testCaseId, login, ct); // ── GetByScreen — merges summary + test cases + steps ──────────────────── public async Task GetByScreenAsync( string screenRoute, LoginDTO login, CancellationToken ct) { // Summary (aggregates from link table only — tag-based cases counted by query) var summary = await _Dal.GetScreenSummaryAsync(screenRoute, login, ct) .ConfigureAwait(false) ?? new TCMSScreenCoverageDTO { ScreenRoute = screenRoute, ScreenTitle = screenRoute, ModuleName = string.Empty, }; // Test case rows (link table UNION tags — see QB dual-source query) var cases = (await _Dal.GetTestCasesByScreenAsync(screenRoute, login, ct) .ConfigureAwait(false)).ToList(); if (cases.Count > 0) await HydrateStepsAsync(cases, login, ct).ConfigureAwait(false); summary.TestCases = cases; summary.TotalCases = cases.Count; return summary; } // ── GetByApiOperation ───────────────────────────────────────────────────── public async Task GetByApiOperationAsync( long apiOperationId, LoginDTO login, CancellationToken ct) { var cases = (await _Dal.GetTestCasesByApiOperationAsync(apiOperationId, login, ct) .ConfigureAwait(false)).ToList(); if (cases.Count > 0) await HydrateStepsAsync(cases, login, ct).ConfigureAwait(false); return new TCMSApiCoverageDTO { TotalCases = cases.Count, PassCount = cases.Count(c => c.LastRunStatus == "Pass"), FailCount = cases.Count(c => c.LastRunStatus == "Fail"), LastRunDate = cases.Max(c => c.LastRunDate), TestCases = cases, }; } // ── Shared helper — bulk-load steps and attach to case list ────────────── private async Task HydrateStepsAsync( List cases, LoginDTO login, CancellationToken ct) { var steps = await _Dal.GetStepsForCasesAsync( cases.Select(c => c.TestCaseId), login, ct).ConfigureAwait(false); var stepsByCase = steps.GroupBy(s => s.TestCaseId) .ToDictionary(g => g.Key, g => (IEnumerable)g.ToList()); foreach (var tc in cases) tc.Steps = stepsByCase.TryGetValue(tc.TestCaseId, out var s) ? s : Enumerable.Empty(); } // ── Screen tree ─────────────────────────────────────────────────────────── public Task> GetScreenTreeAsync( LoginDTO login, CancellationToken ct) => _Dal.GetScreenTreeAsync(login, ct); // ── Gap detection ───────────────────────────────────────────────────────── public async Task> GetCoverageGapsAsync( LoginDTO login, CancellationToken ct) { var screenGaps = await _Dal.GetScreenGapsAsync(login, ct).ConfigureAwait(false); var apiGaps = await _Dal.GetApiGapsAsync(login, ct).ConfigureAwait(false); return screenGaps.Concat(apiGaps); } }