import {
  collection,
  doc,
  type DocumentData,
  type FirestoreDataConverter,
  type QueryDocumentSnapshot,
  type SnapshotOptions,
} from 'firebase/firestore';

import { db } from '@/lib/firebase';
import type { UserDocument, UserFirestoreDocument } from '@/types/firestore';

type TimestampFieldValue = {
  toDate: () => Date;
};

function hasToDate(value: unknown): value is TimestampFieldValue {
  return (
    typeof value === 'object' &&
    value !== null &&
    'toDate' in value &&
    typeof value.toDate === 'function'
  );
}

export function mapTimestampFields<
  Source extends Record<string, unknown>,
  Result extends Record<string, unknown>,
>(data: Source, fields: Array<keyof Source>) {
  const nextData = { ...data } as Record<string, unknown>;

  for (const field of fields) {
    const value = nextData[field as string];

    if (hasToDate(value)) {
      nextData[field as string] = value.toDate();
    }
  }

  return nextData as Result;
}

const converter = <AppModel extends DocumentData, DbModel extends DocumentData>(
  fromFirestoreMapper: (data: DbModel) => AppModel
): FirestoreDataConverter<AppModel> => ({
  toFirestore: (value) => value as DocumentData,
  fromFirestore: (
    snapshot: QueryDocumentSnapshot,
    options: SnapshotOptions
  ) => fromFirestoreMapper(snapshot.data(options) as DbModel),
});

const userConverter = converter<UserDocument, UserFirestoreDocument>((data) =>
  mapTimestampFields<UserFirestoreDocument, UserDocument>(data, [
    'createdAt',
    'updatedAt',
  ])
);

export const collectionNames = {
  users: 'users',
} as const;

export const usersCollectionRef = collection(db, collectionNames.users);

export const usersCollection = usersCollectionRef.withConverter(userConverter);

export const userDoc = (uid: string) => doc(usersCollection, uid);
export const userDocRef = (uid: string) => doc(usersCollectionRef, uid);
