import { serializeError } from '@finalytic/utils';
import { CamelCasePlugin, Kysely as K } from 'kysely';
import { PostgresJSDialect } from 'kysely-postgres-js';
import postgres, { type Sql } from 'postgres';

export type {
  ColumnType,
  Expression,
  ExpressionBuilder,
  ExpressionOrFactory,
  Generated,
  Insertable,
  JSONColumnType,
  OperandExpression,
  Selectable,
  SqlBool,
  Updateable,
} from 'kysely';
export * from './v1.generated';

import { join, relative } from 'node:path';
import type { DB } from './v1.generated';

export { sql } from 'kysely';

export type Kysely = K<DB>;
export type Postgres = Sql;
export function useKysely(postgres: Sql) {
  return new K<DB>({
    dialect: new PostgresJSDialect({
      postgres,
    }),
    plugins: [new CamelCasePlugin()],
  });
}

export function usePostgres(connectionString: string, ssl?: boolean) {
  return postgres(connectionString, {
    ssl: ssl ? 'require' : false,
    max: 1,
  });
}

export class DatabaseError extends Error {
  constructor(message: string, cause: any) {
    super(message);
    this.cause = cause;
  }
}

const dirname =
  typeof __dirname === 'string' ? join(__dirname, '../../..') : '';

export async function withStack<T>(a: Promise<T>) {
  // skip if we can't get the stack, probably in a worker
  if (!dirname) return await a;

  const now = performance.now();
  const error = new DatabaseError('Error in execute', undefined);
  const stack = error.stack?.split('\n').slice(3) || undefined;
  const firstLine =
    stack?.length && stack[0]?.includes('(') && dirname
      ? relative(dirname, stack[0].split('(')[1].split(')')[0])
      : undefined;
  try {
    const result = await a;
    console.log(
      `[${firstLine}] Execution time: ${Math.round(performance.now() - now)}ms`
    );
    return result;
  } catch (e: any) {
    if (stack) error.stack = stack.join('\n').trim();
    else error.stack = e.stack;
    // error.cause = e;
    error.message = e.message;
    const serialized = serializeError(e);
    delete serialized.stack;
    error.cause = serialized;
    console.error(`Error in execute: ${firstLine}`, serializeError(error));
    console.log(
      `[${firstLine}] Execution time: ${Math.round(performance.now() - now)}ms`
    );
    throw error;
  }
}
