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 { CreateTripInvitationFields, TripInvitation } from './trip.types';

function toTripInvitation(row: {
  id: string;
  trip_id: string;
  email: string;
  invited_by_user_id: string;
  token_hash: string;
  expires_at: Date;
  accepted_at: Date | null;
  created_at: Date;
  updated_at: Date;
}): TripInvitation {
  return {
    id: row.id,
    tripId: row.trip_id,
    email: row.email,
    invitedByUserId: row.invited_by_user_id,
    tokenHash: row.token_hash,
    expiresAt: row.expires_at,
    acceptedAt: row.accepted_at,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

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

  async create(
    tripId: string,
    fields: CreateTripInvitationFields,
  ): Promise<TripInvitation> {
    const row = await this.db
      .insertInto('trip_invitations')
      .values({
        trip_id: tripId,
        email: fields.email,
        invited_by_user_id: fields.invitedByUserId,
        token_hash: fields.tokenHash,
        expires_at: fields.expiresAt.toISOString(),
      })
      .returningAll()
      .executeTakeFirstOrThrow();
    return toTripInvitation(row);
  }

  async findByTokenHash(
    tokenHash: string,
  ): Promise<TripInvitation | undefined> {
    const row = await this.db
      .selectFrom('trip_invitations')
      .selectAll()
      .where('token_hash', '=', tokenHash)
      .executeTakeFirst();
    return row ? toTripInvitation(row) : undefined;
  }

  async listByTrip(tripId: string): Promise<TripInvitation[]> {
    const rows = await this.db
      .selectFrom('trip_invitations')
      .selectAll()
      .where('trip_id', '=', tripId)
      .execute();
    return rows.map(toTripInvitation);
  }

  async markAccepted(invitationId: string): Promise<void> {
    await this.db
      .updateTable('trip_invitations')
      .set({
        accepted_at: new Date().toISOString(),
        updated_at: new Date().toISOString(),
      })
      .where('id', '=', invitationId)
      .execute();
  }

  async remove(tripId: string, invitationId: string): Promise<void> {
    await this.db
      .deleteFrom('trip_invitations')
      .where('trip_id', '=', tripId)
      .where('id', '=', invitationId)
      .execute();
  }
}
