import { isArrayColumn, isEnumColumn } from "../../../db/pg-to-lite.js";
import { liteTypeString, upstreamDataType, isArray, isEnum, nullableUpstream } from "../../../types/lite.js";
const CREATE_COLUMN_METADATA_TABLE = `
  CREATE TABLE "_zero.column_metadata" (
    table_name TEXT NOT NULL,
    column_name TEXT NOT NULL,
    upstream_type TEXT NOT NULL,
    is_not_null INTEGER NOT NULL,
    is_enum INTEGER NOT NULL,
    is_array INTEGER NOT NULL,
    character_max_length INTEGER,
    backfill TEXT,
    PRIMARY KEY (table_name, column_name)
  );
`;
class ColumnMetadataStore {
  static #instances = /* @__PURE__ */ new WeakMap();
  #insertStmt;
  #updateStmt;
  #clearBackfillStmt;
  #deleteColumnStmt;
  #deleteTableStmt;
  #renameTableStmt;
  #getColumnStmt;
  #getTableStmt;
  #hasTableStmt;
  constructor(db) {
    this.#insertStmt = db.prepare(`
      INSERT INTO "_zero.column_metadata"
        (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    `);
    this.#updateStmt = db.prepare(`
      UPDATE "_zero.column_metadata"
      SET column_name = ?,
          upstream_type = ?,
          is_not_null = ?,
          is_enum = ?,
          is_array = ?,
          character_max_length = ?
      WHERE table_name = ? AND column_name = ?
    `);
    this.#clearBackfillStmt = db.prepare(
      /*sql*/
      `
      UPDATE "_zero.column_metadata"
      SET backfill = NULL
      WHERE table_name = ? AND column_name = ?
    `
    );
    this.#deleteColumnStmt = db.prepare(`
      DELETE FROM "_zero.column_metadata"
      WHERE table_name = ? AND column_name = ?
    `);
    this.#deleteTableStmt = db.prepare(`
      DELETE FROM "_zero.column_metadata"
      WHERE table_name = ?
    `);
    this.#renameTableStmt = db.prepare(`
      UPDATE "_zero.column_metadata"
      SET table_name = ?
      WHERE table_name = ?
    `);
    this.#getColumnStmt = db.prepare(`
      SELECT upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill
      FROM "_zero.column_metadata"
      WHERE table_name = ? AND column_name = ?
    `);
    this.#getTableStmt = db.prepare(`
      SELECT column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length, backfill
      FROM "_zero.column_metadata"
      WHERE table_name = ?
      ORDER BY column_name
    `);
    this.#hasTableStmt = db.prepare(`
      SELECT 1 FROM sqlite_master
      WHERE type = 'table' AND name = '_zero.column_metadata'
    `);
  }
  /**
   * Gets the singleton instance of ColumnMetadataStore for the given database.
   * Returns `undefined` if the metadata table doesn't exist yet.
   */
  static getInstance(db) {
    const tableExists = db.prepare(
      `SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = '_zero.column_metadata'`
    ).get();
    if (!tableExists) {
      return void 0;
    }
    let instance = ColumnMetadataStore.#instances.get(db);
    if (!instance) {
      instance = new ColumnMetadataStore(db);
      ColumnMetadataStore.#instances.set(db, instance);
    }
    return instance;
  }
  insert(tableName, columnName, spec, backfill) {
    const metadata = pgColumnSpecToMetadata(spec);
    this.#insertMetadata(tableName, columnName, metadata, backfill);
  }
  #insertMetadata(tableName, columnName, metadata, backfill) {
    this.#insertStmt.run(
      tableName,
      columnName,
      metadata.upstreamType,
      metadata.isNotNull ? 1 : 0,
      metadata.isEnum ? 1 : 0,
      metadata.isArray ? 1 : 0,
      metadata.characterMaxLength ?? null,
      backfill ? JSON.stringify(backfill) : null
    );
  }
  update(tableName, oldColumnName, newColumnName, spec) {
    const metadata = pgColumnSpecToMetadata(spec);
    this.#updateStmt.run(
      newColumnName,
      metadata.upstreamType,
      metadata.isNotNull ? 1 : 0,
      metadata.isEnum ? 1 : 0,
      metadata.isArray ? 1 : 0,
      metadata.characterMaxLength ?? null,
      tableName,
      oldColumnName
    );
  }
  clearBackfilling(tableName, columnName) {
    this.#clearBackfillStmt.run(tableName, columnName);
  }
  deleteColumn(tableName, columnName) {
    this.#deleteColumnStmt.run(tableName, columnName);
  }
  deleteTable(tableName) {
    this.#deleteTableStmt.run(tableName);
  }
  renameTable(oldTableName, newTableName) {
    this.#renameTableStmt.run(newTableName, oldTableName);
  }
  getColumn(tableName, columnName) {
    const row = this.#getColumnStmt.get(tableName, columnName);
    if (!row) {
      return void 0;
    }
    return {
      upstreamType: row.upstream_type,
      isNotNull: row.is_not_null !== 0,
      isEnum: row.is_enum !== 0,
      isArray: row.is_array !== 0,
      characterMaxLength: row.character_max_length,
      isBackfilling: row.backfill !== null
    };
  }
  getTable(tableName) {
    const rows = this.#getTableStmt.all(tableName);
    const metadata = /* @__PURE__ */ new Map();
    for (const row of rows) {
      metadata.set(row.column_name, {
        upstreamType: row.upstream_type,
        isNotNull: row.is_not_null !== 0,
        isEnum: row.is_enum !== 0,
        isArray: row.is_array !== 0,
        characterMaxLength: row.character_max_length,
        isBackfilling: row.backfill !== null
      });
    }
    return metadata;
  }
  hasTable() {
    const result = this.#hasTableStmt.get();
    return result !== void 0;
  }
}
function populateFromExistingTables(db, tables) {
  const legacyInsertStmt = db.prepare(`
      INSERT INTO "_zero.column_metadata"
        (table_name, column_name, upstream_type, is_not_null, is_enum, is_array, character_max_length)
      VALUES (?, ?, ?, ?, ?, ?, ?)
    `);
  for (const table of tables) {
    for (const [columnName, columnSpec] of Object.entries(table.columns)) {
      const metadata = liteTypeStringToMetadata(
        columnSpec.dataType,
        columnSpec.characterMaximumLength
      );
      legacyInsertStmt.run(
        table.name,
        columnName,
        metadata.upstreamType,
        metadata.isNotNull ? 1 : 0,
        metadata.isEnum ? 1 : 0,
        metadata.isArray ? 1 : 0,
        metadata.characterMaxLength ?? null
      );
    }
  }
}
function liteTypeStringToMetadata(liteTypeString2, characterMaxLength) {
  const baseType = upstreamDataType(liteTypeString2);
  const isArrayType = isArray(liteTypeString2);
  const fullUpstreamType = isArrayType && !baseType.includes("[]") ? `${baseType}[]` : baseType;
  return {
    upstreamType: fullUpstreamType,
    isNotNull: !nullableUpstream(liteTypeString2),
    isEnum: isEnum(liteTypeString2),
    isArray: isArrayType,
    characterMaxLength: characterMaxLength ?? null,
    isBackfilling: false
  };
}
function metadataToLiteTypeString(metadata) {
  return liteTypeString(
    metadata.upstreamType,
    metadata.isNotNull,
    metadata.isEnum,
    metadata.isArray
  );
}
function pgColumnSpecToMetadata(spec) {
  return {
    upstreamType: spec.dataType,
    isNotNull: spec.notNull ?? false,
    isEnum: isEnumColumn(spec),
    isArray: isArrayColumn(spec),
    characterMaxLength: spec.characterMaximumLength ?? null,
    isBackfilling: false
  };
}
export {
  CREATE_COLUMN_METADATA_TABLE,
  ColumnMetadataStore,
  liteTypeStringToMetadata,
  metadataToLiteTypeString,
  pgColumnSpecToMetadata,
  populateFromExistingTables
};
//# sourceMappingURL=column-metadata.js.map
