namespace Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
///
/// 入站抽数(PULL)的工厂作用域语义 —— 与 严格同源。
///
/// 背景:部分源表(b_examine_result / b_bom_child_examine / srm_pr_main / ic_demandschedule 等)
/// 把 factory_id 写成了 tenant_id(见 Order/ResourceCheckResultWriter:entry.FactoryId ?? order.FactoryId ?? entry.TenantId),
/// 这类值并不是真实工厂,只是"没有工厂"的另一种写法。写入层 早已按
/// NormalizeFactoryId 把 factory_id==tenant_id / 0 / NULL 统一归一成"无工厂"→回落到默认工厂 1;
/// 若拉取层不做同样的归一,就会出现"拉取层判定不匹配→0 行,写入层却认为匹配"的口径分裂,
/// 表现为长期 rows_read=0 但 status=SUCCESS 的静默空跑。
///
/// 因此本类不新造口径,只把写入层已有的归一规则翻译成等价 SQL,保证两层永远一致。
///
/// 与 的区别(勿混用):
/// 后者服务于 DWD/KPI **读取**侧,对 factory_id==tenant_id 的行采取"排除"策略
/// (谓词里带 col <> @tenantId),因为那是脏数据不应计入运营事实;
/// 而入站 PULL 侧必须"归一"而非"排除",否则整张源表会被丢光。两者语义方向相反,不可互相替换。
///
internal static class MdpFactoryScope
{
/// 历史/无工厂数据统一回落到的默认工厂号(与 MdpStagingWriter 的 resolvedFactory 回落一致)。
internal const long LegacyDefaultFactoryId = 1;
///
/// 把源行的 factory_id 归一成可比较的工厂号:
/// NULL / <=0 / 等于 tenant_id 一律视为"无真实工厂",回落 。
///
internal static long Resolve(long tenantId, long? rowFactoryId)
=> MdpStagingWriter.NormalizeFactoryId(tenantId, rowFactoryId) ?? LegacyDefaultFactoryId;
/// 源行是否落在 (tenantId, scopeFactoryId) 作用域内(工厂维度)。
internal static bool Matches(long tenantId, long scopeFactoryId, long? rowFactoryId)
=> Resolve(tenantId, rowFactoryId) == scopeFactoryId;
///
/// 的等价 SQL(MySQL / SQL Server 通用)。
/// 需已按目标方言转义。
/// CASE WHEN col > 0 THEN col END 而非 NULLIF(col,0) 是刻意的:
/// NormalizeFactoryId 对负数同样返回 null,用 CASE 才能与之逐值等价。
///
internal static string ResolveSqlExpression(string quotedColumn, string tenantParam)
=> $"COALESCE(NULLIF(CASE WHEN {quotedColumn} > 0 THEN {quotedColumn} END, {tenantParam}), {LegacyDefaultFactoryId})";
/// 工厂作用域谓词。注意:引用了 ,调用方必须保证该参数已绑定。
internal static string SqlPredicate(string quotedColumn, string tenantParam, string factoryParam)
=> $"{ResolveSqlExpression(quotedColumn, tenantParam)} = {factoryParam}";
}