import { Database } from "sql.js";

import { CardData, CardMeta, CardText } from "@/api";

import { isNil } from "lodash-es";

import { constructCardMeta } from ".";
const TYPE_MONSTER = 0x1;

/** 过滤条件 */
export interface FtsConditions {
  types: number[]; // 卡片类型
  levels: number[]; // 星阶/刻度/link值
  races: number[]; // 种族
  attributes: number[]; // 属性
  atk: { min: number | null; max: number | null }; // 攻击力区间
  def: { min: number | null; max: number | null }; // 防御力区间
}
export interface FtsParams {
  query: string; // 用于全文检索的query
  conditions: FtsConditions; // 过滤条件
}

export function invokeFts(db: Database, params: FtsParams): CardMeta[] {
  const { query, conditions } = params;
  const ftsTexts: CardText[] = [];
  const ftsMetas: CardMeta[] = [];

  const textStmt = db.prepare("SELECT * FROM texts WHERE name LIKE $query");
  textStmt.bind({ $query: `%${query}%` });

  while (textStmt.step()) {
    const row = textStmt.getAsObject();
    ftsTexts.push(row);
  }

  for (const text of ftsTexts) {
    const id = text.id;
    if (id) {
      const filterConditions = getFtsCondtions(conditions);
      const sql = `SELECT * FROM datas WHERE ${
        filterConditions == "" ? "ID = $id" : `ID = $id AND ${filterConditions}`
      }`;
      const dataStmt = db.prepare(sql);
      const data: CardData = dataStmt.getAsObject({ $id: id });
      if (Object.values(data).filter((v) => v !== undefined).length > 0) {
        ftsMetas.push(constructCardMeta(id, data, text));
      }
    }
  }

  return ftsMetas;
}

function getFtsCondtions(conditions: FtsConditions): string {
  const { types, levels, atk, def, races, attributes } = conditions;
  const assertMonster = `(type & ${TYPE_MONSTER}) > 0`;

  const typesCondition = types
    ?.map((type) => `(type & ${type}) > 0`)
    .join(" OR ");
  const levelsCondition = levels
    ?.map((level) => `level = ${level}`)
    .join(" OR ");
  const atkCondition = atk
    ? `atk BETWEEN ${handleFinite(atk.min, "min")} AND ${handleFinite(
        atk.max,
        "max"
      )} AND ${assertMonster}`
    : undefined;
  const defCondition = def
    ? `def BETWEEN ${handleFinite(def.min, "min")} AND ${handleFinite(
        def.max,
        "max"
      )} AND ${assertMonster}`
    : undefined;
  const raceCondition = races?.map((race) => `race = ${race}`).join(" OR ");
  const attributeCondition = attributes
    ?.map((attribute) => `attribute = ${attribute}`)
    .join(" OR ");

  const merged = [
    typesCondition,
    levelsCondition,
    atkCondition,
    defCondition,
    raceCondition,
    attributeCondition,
  ]
    .filter((condition) => condition !== undefined && condition !== "")
    .map((condition) => `(${condition})`)
    .join(" AND ");

  return merged;
}

function handleFinite(value: number | null, type: "min" | "max"): number {
  if (isNil(value)) return type === "min" ? -2 : 9999999;
  return value;
}
