import { int, mysqlTable, varchar } from 'drizzle-orm/mysql-core';
import type { MySql2Database } from 'drizzle-orm/mysql2';
import { eq } from 'drizzle-orm';
import { Permissions } from '@util/permissions.ts';
import * as bcrypt from 'bcrypt';

type Database = MySql2Database<{ admin: typeof admin }>;

export const admin = mysqlTable('admin', {
  id: int('id').primaryKey().autoincrement(),
  username: varchar('username', { length: 255 }).notNull(),
  password: varchar('password', { length: 255 }).notNull(),
  permissions: int('permissions').notNull()
});

export type AddAdminReq = Omit<typeof admin.$inferInsert, 'id'>;

export type EditAdminReq = {
  id: number;
  username: string;
  password: string | null;
  permissions: number;
};

export type DeleteAdminReq = {
  id: number;
};

export type GetAdminReq = {};
export type GetAdminRes = Omit<typeof admin.$inferSelect, 'password'>[];

export type ExistsAdminReq = {
  username: string;
  password: string;
};
export type ExistsAdminRes = {
  id: number;
  username: string;
  permissions: Permissions;
} | null;

export async function addAdmin(db: Database, values: AddAdminReq) {
  values.password = bcrypt.hashSync(values.password, 10);

  const adminIds = await db.insert(admin).values(values).$returningId();

  return adminIds[0];
}

export async function editAdmin(db: Database, values: EditAdminReq) {
  return db
    .update(admin)
    .set({
      id: values.id,
      username: values.username,
      password: values.password != null ? bcrypt.hashSync(values.password, 10) : undefined,
      permissions: values.permissions
    })
    .where(eq(admin.id, values.id));
}

export async function deleteAdmin(db: Database, values: DeleteAdminReq) {
  return db.delete(admin).where(eq(admin.id, values.id));
}

export async function getAdmins(db: Database, _values: GetAdminReq): Promise<GetAdminRes> {
  return db.select({ id: admin.id, username: admin.username, permissions: admin.permissions }).from(admin);
}

export async function existsAdmin(db: Database, values: ExistsAdminReq): Promise<ExistsAdminRes> {
  const a = await db.query.admin.findFirst({
    where: eq(admin.username, values.username)
  });

  if (!a || !bcrypt.compareSync(values.password, a.password)) return null;

  return {
    id: a.id,
    username: a.username,
    permissions: new Permissions(a.permissions)
  };
}
