import { Inject, Injectable } from '@nestjs/common';
import type { Kysely } from 'kysely';
import { KYSELY_DB } from '../../database/src';
import type { Database } from '../../database/src';
import type { User, UserClaims } from './user.types';

function toUser(row: {
  id: string;
  external_subject_id: string;
  display_name: string;
  email: string;
  created_at: Date;
  updated_at: Date;
}): User {
  return {
    id: row.id,
    externalSubjectId: row.external_subject_id,
    displayName: row.display_name,
    email: row.email,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

@Injectable()
export class UsersRepository {
  constructor(@Inject(KYSELY_DB) private readonly db: Kysely<Database>) {}

  async upsertByExternalSubjectId(
    externalSubjectId: string,
    claims: UserClaims,
  ): Promise<User> {
    const row = await this.db
      .insertInto('users')
      .values({
        external_subject_id: externalSubjectId,
        display_name: claims.displayName,
        email: claims.email,
      })
      .onConflict((oc) =>
        oc.column('external_subject_id').doUpdateSet({
          display_name: claims.displayName,
          email: claims.email,
          updated_at: new Date().toISOString(),
        }),
      )
      .returningAll()
      .executeTakeFirstOrThrow();

    return toUser(row);
  }

  async findById(id: string): Promise<User | undefined> {
    const row = await this.db
      .selectFrom('users')
      .selectAll()
      .where('id', '=', id)
      .executeTakeFirst();
    return row ? toUser(row) : undefined;
  }
}
