import type { Kysely } from "kysely";

/**
 * Console-wide settings storage.
 *
 * `console_settings` is newer than the last kysely-codegen run, so it is absent
 * from the generated `DB` type. Following the same approach as
 * `promo-code-campaigns.ts`, we work against an untyped `Kysely<any>` rather
 * than hand-editing the generated file (which says not to, and which would lose
 * the edit on the next codegen).
 */

export interface ConsoleSettingRow {
  key: string;
  value: unknown;
  updated_at: string | Date | null;
  updated_by: string | null;
}

export class ConsoleSettingsRepository {
  private db: Kysely<any>;

  constructor(db: unknown) {
    this.db = db as Kysely<any>;
  }

  /** The stored setting, or null when it has never been written. */
  async get(key: string): Promise<ConsoleSettingRow | null> {
    const row = await this.db
      .selectFrom("console_settings")
      .select(["key", "value", "updated_at", "updated_by"])
      .where("key", "=", key)
      .executeTakeFirst();
    return (row as ConsoleSettingRow | undefined) ?? null;
  }

  /**
   * Writes the setting, replacing any existing value for the key.
   *
   * An upsert on the unique `key` rather than a read-then-insert: two admins
   * saving at once would otherwise race, and one insert would fail on the
   * constraint instead of simply taking the later value.
   */
  async put(
    key: string,
    value: unknown,
    updatedBy: string | null,
  ): Promise<ConsoleSettingRow> {
    const row = await this.db
      .insertInto("console_settings")
      .values({
        key,
        value: JSON.stringify(value),
        updated_by: updatedBy,
        updated_at: new Date(),
      })
      .onConflict((oc: any) =>
        oc.column("key").doUpdateSet({
          value: JSON.stringify(value),
          updated_by: updatedBy,
          updated_at: new Date(),
        }),
      )
      .returning(["key", "value", "updated_at", "updated_by"])
      .executeTakeFirstOrThrow();
    return row as ConsoleSettingRow;
  }
}
