import type { ColdImportManifest } from './manifest.schema';

/**
 * TenantResolver — 把 host 行解析到 PAC tenant_id。
 *
 * **3 种工作模式**:
 *   1. 静态:manifest.tenant_id 配置 → 所有行返回同一 tenant
 *   2. 字段映射:manifest.tenant_field + tenant_map → 按行内字段值查映射表
 *   3. 兼容旧 brand_to_tenant → 等价于 tenant_field='brand' + tenant_map=<map>
 *
 * **未匹配的行**:resolve() 返回 null,caller(cold-import)应跳过该行 + warn 日志。
 *
 * 设计意图(W3 末重构):
 *   原 cold-import 写死"一份 manifest = 一个 tenant",多 brand 必须跑两次。
 *   实际 PAC schema 早支持 `UNIQUE(host_id, tenant_id, external_id)` 多 tenant per host,
 *   只是 cold-import 当年图简单没用上。本次解放成 per-row 数据驱动 tenant 路由。
 */
export interface TenantResolver {
  /** 解析一行的 tenant_id;未匹配返回 null */
  resolve(row: Record<string, unknown>): string | null;
  /** 描述本 resolver(日志用)*/
  describe(): string;
  /** 已知 / 静态 tenant 集合(动态 pass-through 模式返回空数组) */
  knownTenants(): string[];
}

class StaticTenantResolver implements TenantResolver {
  constructor(private readonly tenantId: string) {}
  resolve(): string {
    return this.tenantId;
  }
  describe(): string {
    return `static(${this.tenantId})`;
  }
  knownTenants(): string[] {
    return [this.tenantId];
  }
}

/**
 * 字段值直接当 tenant_id(纯 pass-through)— **不预设 host 数据范围**。
 * row.brand='瑞尔' → tenant_id='瑞尔' / row.brand='瑞泰' → '瑞泰'。
 * 数据来什么就是什么,PAC 不强制翻译。
 */
class FieldPassThroughResolver implements TenantResolver {
  constructor(private readonly field: string) {}
  resolve(row: Record<string, unknown>): string | null {
    const v = row[this.field];
    if (v === undefined || v === null) return null;
    const s = String(v).trim();
    return s.length > 0 ? s : null;
  }
  describe(): string {
    return `field(${this.field}, pass-through)`;
  }
  knownTenants(): string[] {
    // 动态:依数据而定,不可预知
    return [];
  }
}

/**
 * 字段值查 map(显式映射,W3 末降级为兼容选项)— 主要给 brand_to_tenant 老 yaml 用。
 * map 命中 → 用映射值;未命中 → 用字段原值兜底(不再返回 null,符合"数据来什么就是什么")。
 */
class FieldMapTenantResolver implements TenantResolver {
  constructor(
    private readonly field: string,
    private readonly map: Record<string, string>,
  ) {}
  resolve(row: Record<string, unknown>): string | null {
    const v = row[this.field];
    if (v === undefined || v === null) return null;
    const s = String(v).trim();
    if (s.length === 0) return null;
    return this.map[s] ?? s; // 未命中时 fallback 用原值(pass-through)
  }
  describe(): string {
    const pairs = Object.entries(this.map)
      .map(([k, v]) => `${k}→${v}`)
      .join(',');
    return `field(${this.field}: ${pairs}; 未命中 pass-through)`;
  }
  knownTenants(): string[] {
    return [...new Set(Object.values(this.map))];
  }
}

/**
 * 按 manifest 构建合适的 resolver。
 */
export function buildTenantResolver(manifest: ColdImportManifest): TenantResolver {
  // 1. tenant_field + tenant_map(显式映射;未命中 pass-through 用原值)
  if (manifest.tenant_field && manifest.tenant_map) {
    return new FieldMapTenantResolver(manifest.tenant_field, manifest.tenant_map);
  }
  // 2. tenant_field 单字段(pure pass-through,W3 末推荐)
  if (manifest.tenant_field) {
    return new FieldPassThroughResolver(manifest.tenant_field);
  }
  // 3. 旧 brand_to_tenant(兼容)— 默认 field='brand'
  if (manifest.brand_to_tenant) {
    return new FieldMapTenantResolver('brand', manifest.brand_to_tenant);
  }
  // 4. tenant_id(静态)
  if (manifest.tenant_id) {
    return new StaticTenantResolver(manifest.tenant_id);
  }
  throw new Error(
    'buildTenantResolver: manifest 必须含 tenant_id / tenant_field / brand_to_tenant 之一',
  );
}

/**
 * SourceUnitResolver — 从源行解析 patients.source_unit(发证命名空间)。
 *
 * 与 tenant 解耦:tenant=集团(静态),source_unit=源行的命名空间字段值(如 brand)。
 * manifest 未声明 identity_namespace_field → 恒返回 ''(单命名空间 host,external_id 集团内本就唯一)。
 * 返回 '' 而非 null:source_unit 是 NOT NULL 列,'' = 无命名空间。
 */
export interface SourceUnitResolver {
  resolve(row: Record<string, unknown>): string;
}

export function buildSourceUnitResolver(manifest: ColdImportManifest): SourceUnitResolver {
  const field = manifest.identity_namespace_field;
  if (!field) {
    return { resolve: () => '' };
  }
  return {
    resolve(row: Record<string, unknown>): string {
      const v = row[field];
      if (v === undefined || v === null) return '';
      return String(v).trim();
    },
  };
}
