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 {
  CreateTravelerDto,
  Traveler,
  TravelerType,
  UpdateTravelerDto,
} from './trip.types';

function toTraveler(row: {
  id: string;
  trip_id: string;
  linked_user_id: string | null;
  display_name: string;
  traveler_type: string;
  created_by_user_id: string;
  created_at: Date;
  updated_at: Date;
}): Traveler {
  return {
    id: row.id,
    tripId: row.trip_id,
    linkedUserId: row.linked_user_id,
    displayName: row.display_name,
    travelerType: row.traveler_type as TravelerType,
    createdByUserId: row.created_by_user_id,
    createdAt: row.created_at,
    updatedAt: row.updated_at,
  };
}

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

  async create(
    tripId: string,
    dto: CreateTravelerDto,
    createdByUserId: string,
  ): Promise<Traveler> {
    const row = await this.db
      .insertInto('travelers')
      .values({
        trip_id: tripId,
        linked_user_id: dto.linkedUserId ?? null,
        display_name: dto.displayName,
        traveler_type: dto.travelerType,
        created_by_user_id: createdByUserId,
      })
      .returningAll()
      .executeTakeFirstOrThrow();
    return toTraveler(row);
  }

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

  async update(
    tripId: string,
    travelerId: string,
    dto: UpdateTravelerDto,
  ): Promise<Traveler | undefined> {
    const patch: {
      display_name?: string;
      traveler_type?: TravelerType;
      linked_user_id?: string | null;
    } = {};
    if (dto.displayName !== undefined) patch.display_name = dto.displayName;
    if (dto.travelerType !== undefined) patch.traveler_type = dto.travelerType;
    if (dto.linkedUserId !== undefined) patch.linked_user_id = dto.linkedUserId;

    const row = await this.db
      .updateTable('travelers')
      .set({ ...patch, updated_at: new Date().toISOString() })
      .where('trip_id', '=', tripId)
      .where('id', '=', travelerId)
      .returningAll()
      .executeTakeFirst();
    return row ? toTraveler(row) : undefined;
  }

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