import type { FilterOp, WhereRule } from '../transforms.schema';
import { isEmptyText, type Row } from '../row';

/**
 * filter — 按 where 规则保留行(等价 SQL WHERE 行级过滤)。
 *
 * where 是 AND 关系:所有字段规则都通过才保留。
 *
 * 规则:
 *   - not_empty: true   — 字段非 null/undefined/空字符串
 *   - in: [...]         — 字段值在列表中
 *   - equals: x         — 字段值等于 x
 */
export function runFilter(op: FilterOp, rows: Row[]): { rows: Row[]; dropped: number } {
  const out: Row[] = [];
  let dropped = 0;
  for (const row of rows) {
    if (passAll(row, op.where)) {
      out.push(row);
    } else {
      dropped++;
    }
  }
  return { rows: out, dropped };
}

/// "空"判定(给 where.empty 用):null/""/空白/"[]",或【空壳 JSON 数组】——
/// 数组元素全无实质内容(所有非 *Bak base64、非数组的标量字段都空)。
/// host EMR 常见占位行 [{treatName:"", toothPositionBak:"<base64>"}] 视为空,用于 gate "无结构化治疗"。
function isBlankValue(v: unknown): boolean {
  if (isEmptyText(v)) return true;
  if (typeof v !== 'string') return false;
  const t = v.trim();
  if (t === '[]') return true;
  if (!t.startsWith('[')) return false;
  try {
    const arr = JSON.parse(t);
    if (!Array.isArray(arr)) return false;
    const hasContent = arr.some(
      (el) =>
        el &&
        typeof el === 'object' &&
        Object.entries(el as Record<string, unknown>).some(
          ([k, val]) => !k.endsWith('Bak') && typeof val === 'string' && val.trim() !== '',
        ),
    );
    return !hasContent;
  } catch {
    return false;
  }
}

/// blank_or_all_in:空,或 JSON 数组里**每个**元素的 treatName ∈ 列表(空 treatName 也算通过)。
/// 用于 dispose gate:treat_plan 空 / 全是复查类(无真治疗)→ 才去 dispose 抽治疗(陈昱天案例)。
/// 任一元素 treatName 不在列表(= 真治疗)→ false → 不抽 dispose(避免与结构化重复)。
/// 匹配 = trim 后精确(对齐 C.3 route 的 equals 语义;review 词多无标点,raw≈normalize)。
function isBlankOrAllIn(v: unknown, list: Array<string | number>): boolean {
  if (isBlankValue(v)) return true;
  if (typeof v !== 'string') return false;
  const t = v.trim();
  if (!t.startsWith('[')) return false;
  let arr: unknown;
  try {
    arr = JSON.parse(t);
  } catch {
    return false;
  }
  if (!Array.isArray(arr)) return false;
  if (arr.length === 0) return true;
  const set = new Set(list.map((x) => String(x).trim()));
  return arr.every((el) => {
    if (!el || typeof el !== 'object') return true;
    const name = String((el as Record<string, unknown>).treatName ?? '').trim();
    return name === '' || set.has(name);
  });
}

/// real_no_tooth:JSON 数组里【有 ≥1 个真治疗(treatName 非空且 ∉ review 列表)】
///   **且 没有任何元素带牙位(toothPosition 全空)**。
/// 用于 dispose gate 的 path 2:treat_plan 有真治疗(充填)但牙位漏填(刘小源"充填"无牙位)→
///   treat_plan 给不出牙位 → 去 dispose 找回牙位。
/// 刻意排除:① 空 / 全复查(交 path 1 的 blank_or_all_in 处理,避免两路重复抽 dispose 双计);
///          ② 任一元素已带牙位(treat_plan 牙位齐全 → 不抽,避免同次重复)。
function isRealNoTooth(v: unknown, list: Array<string | number>): boolean {
  if (typeof v !== 'string') return false;
  const t = v.trim();
  if (!t.startsWith('[')) return false;
  let arr: unknown;
  try {
    arr = JSON.parse(t);
  } catch {
    return false;
  }
  if (!Array.isArray(arr) || arr.length === 0) return false;
  const review = new Set(list.map((x) => String(x).trim()));
  let hasReal = false;
  let anyTooth = false;
  for (const el of arr) {
    if (!el || typeof el !== 'object') continue;
    const o = el as Record<string, unknown>;
    const name = String(o.treatName ?? '').trim();
    const tooth = String(o.toothPosition ?? '').trim();
    if (name !== '' && !review.has(name)) hasReal = true;
    if (tooth !== '') anyTooth = true;
  }
  return hasReal && !anyTooth;
}

function passAll(row: Row, where: Record<string, WhereRule>): boolean {
  for (const [field, rule] of Object.entries(where)) {
    const v = row[field];
    if ('not_empty' in rule) {
      if (isEmptyText(v)) return false;
    } else if ('empty' in rule) {
      // 空:null/""/空白/"[]" 或【空壳 JSON 数组】(元素全无实质内容,如 host 占位行
      //   [{treatName:"",toothPosition:null,toothPositionBak:"<base64>"}])。
      // 用于 gate "无结构化治疗" — treat_plan 空壳时也应去 dispose 找治疗。
      if (!isBlankValue(v)) return false;
    } else if ('blank_or_all_in' in rule) {
      // 空 或 JSON 数组每个元素 treatName ∈ 列表 → 通过(treat_plan 无真治疗 → 去 dispose 抽)。
      if (!isBlankOrAllIn(v, rule.blank_or_all_in)) return false;
    } else if ('real_no_tooth' in rule) {
      // treat_plan 有真治疗但全无牙位 → 通过(去 dispose 找回牙位;path 2)。
      if (!isRealNoTooth(v, rule.real_no_tooth)) return false;
    } else if ('in' in rule) {
      const set = new Set(rule.in.map((x) => String(x)));
      if (!set.has(String(v))) return false;
    } else if ('equals' in rule) {
      if (String(v) !== String(rule.equals)) return false;
    }
  }
  return true;
}
