import { CalendarEvent, CreateEventDTO, UpdateEventDTO } from "@calchat/shared";
import { EventRepository } from "../../services/interfaces";
import { Logged } from "../../logging";
import { EventModel } from "./models";

@Logged("MongoEventRepository")
export class MongoEventRepository implements EventRepository {
  async findById(id: string): Promise<CalendarEvent | null> {
    const event = await EventModel.findById(id);
    if (!event) return null;
    return event.toJSON() as unknown as CalendarEvent;
  }

  async findByUserId(userId: string): Promise<CalendarEvent[]> {
    const events = await EventModel.find({ userId }).sort({ startTime: 1 });
    return events.map((e) => e.toJSON() as unknown as CalendarEvent);
  }

  async findByDateRange(
    userId: string,
    startDate: Date,
    endDate: Date,
  ): Promise<CalendarEvent[]> {
    const events = await EventModel.find({
      userId,
      startTime: { $gte: startDate, $lte: endDate },
    }).sort({ startTime: 1 });
    return events.map((e) => e.toJSON() as unknown as CalendarEvent);
  }

  async findByCaldavUUID(
    userId: string,
    caldavUUID: string,
  ): Promise<CalendarEvent | null> {
    const event = await EventModel.findOne({ userId, caldavUUID });
    if (!event) return null;
    return event.toJSON() as unknown as CalendarEvent;
  }

  async searchByTitle(userId: string, query: string): Promise<CalendarEvent[]> {
    const events = await EventModel.find({
      userId,
      title: { $regex: query, $options: "i" },
    }).sort({ startTime: 1 });
    return events.map((e) => e.toJSON() as unknown as CalendarEvent);
  }

  async create(userId: string, data: CreateEventDTO): Promise<CalendarEvent> {
    const event = await EventModel.create({ userId, ...data });
    // NOTE: Casting required because Mongoose's toJSON() type doesn't reflect our virtual 'id' field
    return event.toJSON() as unknown as CalendarEvent;
  }

  async update(
    id: string,
    data: UpdateEventDTO,
  ): Promise<CalendarEvent | null> {
    const event = await EventModel.findByIdAndUpdate(id, data, { new: true });
    if (!event) return null;
    return event.toJSON() as unknown as CalendarEvent;
  }

  async delete(id: string): Promise<boolean> {
    const result = await EventModel.findByIdAndDelete(id);
    return result !== null;
  }

  async addExceptionDate(
    id: string,
    date: string,
  ): Promise<CalendarEvent | null> {
    const event = await EventModel.findByIdAndUpdate(
      id,
      { $addToSet: { exceptionDates: date } },
      { new: true },
    );
    if (!event) return null;
    return event.toJSON() as unknown as CalendarEvent;
  }
}
