using System;
using System.Collections.Generic;
using System.Linq;
using AnalyticsDAL.DTO.Wizard;
using GB5Shared.DTO.Framework.SchemaIntrospection;
namespace AnalyticsBLL.Wizard
{
/// Pure logic, no DI, no DB calls — derives candidate DBJOIN suggestions from live
/// foreign-key metadata for whichever tables the wizard user has already selected. Never
/// called automatically by AnalyticsWizardBLL.QuickBuildAsync (which only persists joins the
/// user has explicitly accepted via AnalyticsWizardBuildRequestDTO.AcceptedJoins) — this is a
/// standalone helper the wizard FE (or a future BLL step) can invoke once it knows which
/// tables are selected, since WizardDiscoverRequestDTO itself carries no table selection.
public static class JoinSuggestionService
{
///
/// Builds one SuggestedJoinDTO per foreign-key constraint where both the referencing and
/// referenced table are present in . Multi-column
/// FK constraints are grouped by ConstraintName and combined into a single
/// " AND "-joined expression.
///
///
/// Maps "schema.table" (e.g. "dbo.TCUSTOMER") to the DBObjectId already registered/resolved
/// for that table in this wizard session. MUST use
/// as its key comparer — lookups here are
/// case-insensitive to match SQL Server's default identifier collation.
///
/// The raw FK list from AnalyticsWizardBLL.DiscoverAsync.
public static List SuggestJoins(
IReadOnlyDictionary tableKeyToDbObjectId,
IEnumerable foreignKeys)
{
if (tableKeyToDbObjectId == null) throw new ArgumentNullException(nameof(tableKeyToDbObjectId));
if (foreignKeys == null) throw new ArgumentNullException(nameof(foreignKeys));
var suggestions = new List();
// Group multi-column FKs by ConstraintName so they become one combined expression.
// FKs with no constraint name (defensive — shouldn't happen for real SQL Server FKs)
// each get their own synthetic group so they aren't accidentally merged together.
var groups = foreignKeys
.Select((fk, index) => (fk, index))
.GroupBy(
t => string.IsNullOrWhiteSpace(t.fk.ConstraintName) ? $"__unnamed_{t.index}" : t.fk.ConstraintName,
StringComparer.OrdinalIgnoreCase);
foreach (var group in groups)
{
var columnPairs = group.Select(t => t.fk).ToList();
var first = columnPairs[0];
var fromKey = $"{first.FromSchema}.{first.FromTable}";
var toKey = $"{first.ToSchema}.{first.ToTable}";
if (!tableKeyToDbObjectId.TryGetValue(fromKey, out var fromDbObjectId))
continue;
if (!tableKeyToDbObjectId.TryGetValue(toKey, out var toDbObjectId))
continue;
var expression = string.Join(" AND ",
columnPairs.Select(c => $"{c.FromTable}.{c.FromColumn} = {c.ToTable}.{c.ToColumn}"));
var requiresManualAliasing = string.Equals(first.FromTable, first.ToTable, StringComparison.OrdinalIgnoreCase);
suggestions.Add(new SuggestedJoinDTO
{
FromDBObjectId = fromDbObjectId,
ToDBObjectId = toDbObjectId,
FromTable = first.FromTable,
ToTable = first.ToTable,
Expression = expression,
SourceConstraintName = first.ConstraintName,
RequiresManualAliasing = requiresManualAliasing
});
}
return suggestions;
}
}
}