import { ConflictException, Injectable, NotFoundException } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model, Types } from 'mongoose'
import { EventType, EventTypeAssignment, EventTypeDocument, LocationType } from '../../schemas/event-type.schema.js'

export interface EventTypeInput {
  slug: string
  title: string
  description?: string
  durationMinutes: number
  slotIntervalMinutes?: number
  bufferBeforeMinutes?: number
  bufferAfterMinutes?: number
  minimumNoticeMinutes?: number
  maximumDaysInFuture?: number
  availabilityScheduleId: string
  locationType?: LocationType
  locationDetails?: string
  color?: string
  ignoreAllDayEvents?: boolean
  assignment?: EventTypeAssignment
  hostPool?: string[]
}

@Injectable()
export class EventTypesService {
  constructor(@InjectModel(EventType.name) private readonly model: Model<EventTypeDocument>) {}

  list(tenantId: Types.ObjectId, hostId: Types.ObjectId): Promise<EventTypeDocument[]> {
    return this.model.find({ tenantId, hostId }).sort({ title: 1 }).exec()
  }

  async create(tenantId: Types.ObjectId, hostId: Types.ObjectId, input: EventTypeInput): Promise<EventTypeDocument> {
    try {
      return await this.model.create({
        tenantId,
        hostId,
        ...input,
        availabilityScheduleId: new Types.ObjectId(input.availabilityScheduleId),
        ...(input.hostPool ? { hostPool: input.hostPool.map((id) => new Types.ObjectId(id)) } : {}),
      })
    } catch (err: any) {
      if (err?.code === 11000) throw new ConflictException('An event type with that slug already exists for this host.')
      throw err
    }
  }

  async get(tenantId: Types.ObjectId, id: string): Promise<EventTypeDocument> {
    const doc = await this.model.findOne({ _id: id, tenantId }).exec()
    if (!doc) throw new NotFoundException('Event type not found')
    return doc
  }

  // Public resolution by (tenant, host, slug). Only active types are bookable.
  async getActiveBySlug(tenantId: Types.ObjectId, hostId: Types.ObjectId, slug: string): Promise<EventTypeDocument> {
    const doc = await this.model.findOne({ tenantId, hostId, slug, isActive: true }).exec()
    if (!doc) throw new NotFoundException('Event type not found')
    return doc
  }

  async update(tenantId: Types.ObjectId, id: string, input: Partial<EventTypeInput>): Promise<EventTypeDocument> {
    const doc = await this.get(tenantId, id)
    Object.assign(doc, {
      ...input,
      availabilityScheduleId: input.availabilityScheduleId
        ? new Types.ObjectId(input.availabilityScheduleId)
        : doc.availabilityScheduleId,
      ...(input.hostPool ? { hostPool: input.hostPool.map((hid) => new Types.ObjectId(hid)) } : {}),
    })
    return doc.save()
  }

  async remove(tenantId: Types.ObjectId, id: string): Promise<void> {
    const res = await this.model.deleteOne({ _id: id, tenantId }).exec()
    if (res.deletedCount === 0) throw new NotFoundException('Event type not found')
  }
}
