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

function toOverride(row: {
  id: string;
  trip_id: string;
  user_id: string | null;
  traveler_id: string | null;
  overrides: unknown;
  created_at: Date;
  updated_at: Date;
}): TripPreferenceOverride {
  return {
    id: row.id,
    tripId: row.trip_id,
    userId: row.user_id,
    travelerId: row.traveler_id,
    overrides: (row.overrides ?? {}) as Record<string, unknown>,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

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

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

  async upsert(
    tripId: string,
    subject: PreferenceSubject,
    overrides: Record<string, unknown>,
  ): Promise<TripPreferenceOverride> {
    const userId = subject.type === 'USER' ? subject.userId : null;
    const travelerId = subject.type === 'TRAVELER' ? subject.travelerId : null;

    const row = await this.db
      .insertInto('trip_preference_overrides')
      .values({
        trip_id: tripId,
        user_id: userId,
        traveler_id: travelerId,
        overrides: overrides,
      })
      .onConflict((oc) =>
        (subject.type === 'USER'
          ? oc.columns(['trip_id', 'user_id']).where('user_id', 'is not', null)
          : oc
              .columns(['trip_id', 'traveler_id'])
              .where('traveler_id', 'is not', null)
        ).doUpdateSet({
          overrides: overrides,
          updated_at: new Date().toISOString(),
        }),
      )
      .returningAll()
      .executeTakeFirstOrThrow();

    return toOverride(row);
  }

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