import knex, { type Knex } from 'knex';

import { Services } from '../../utils/service.ts';

import type { PostgresDatabase, PostgresRole } from './postgres.types.ts';

type PostgresInstanceOptions = {
  services: Services;
  host: string;
  port?: number;
  user: string;
  password: string;
  database?: string;
};

class PostgresInstance {
  #db: Knex;

  constructor(options: PostgresInstanceOptions) {
    this.#db = knex({
      client: 'pg',
      connection: {
        host: options.host,
        user: options.user,
        password: options.password,
        port: options.port,
        database: options.database,
      },
    });
  }

  public ping = async () => {
    try {
      await this.#db.raw('SELECT 1');
      return;
    } catch (err) {
      return err;
    }
  };

  public upsertRole = async (role: PostgresRole) => {
    const name = role.name;
    const existingRole = await this.#db.raw('SELECT 1 FROM pg_roles WHERE rolname = ?', [name]);

    if (existingRole.rows.length === 0) {
      await this.#db.raw(`CREATE ROLE "${name}" WITH LOGIN PASSWORD '${role.password}'`);
    } else {
      await this.#db.raw(`ALTER ROLE "${name}" WITH PASSWORD '${role.password}'`);
    }
  };

  public upsertDatabase = async (database: PostgresDatabase) => {
    const owner = database.owner;
    const name = database.name;
    const existingDatabase = await this.#db.raw('SELECT * FROM pg_database WHERE datname = ?', [name]);

    if (existingDatabase.rows.length === 0) {
      await this.#db.raw(`CREATE DATABASE "${name}" OWNER "${owner}"`);
    } else {
      await this.#db.raw(`ALTER DATABASE "${name}" OWNER TO "${owner}"`);
    }
  };
}

export { PostgresInstance, type PostgresInstanceOptions };
