import { Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model, Types } from 'mongoose'
import { Booking, BookingDocument, BookingStatus } from '../../schemas/booking.schema.js'
import { Host, HostDocument } from '../../schemas/scheduling-host.schema.js'

// Read-only analytics for the tenant scheduling admin (Phase 3). All figures are
// derived from the bookings collection scoped to a single tenant; nothing here
// mutates state. The per-day series powers a small sparkline-style chart, and the
// per-host breakdown lets an admin see who is carrying the booking load.

const SERIES_DAYS = 30

export interface HostCount {
  hostId: string
  displayName: string
  count: number
}

export interface DayPoint {
  date: string // YYYY-MM-DD (UTC)
  count: number
}

export interface SchedulingOverview {
  totalBookings: number
  upcomingCount: number
  byStatus: Record<BookingStatus, number>
  byHost: HostCount[]
  last30Days: DayPoint[]
}

@Injectable()
export class OverviewService {
  constructor(
    @InjectModel(Booking.name) private readonly bookingModel: Model<BookingDocument>,
    @InjectModel(Host.name) private readonly hostModel: Model<HostDocument>,
  ) {}

  async forTenant(tenantId: Types.ObjectId): Promise<SchedulingOverview> {
    const now = new Date()
    // Window start = midnight UTC, (SERIES_DAYS - 1) days ago, so the series
    // contains exactly SERIES_DAYS calendar days ending today.
    const windowStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()))
    windowStart.setUTCDate(windowStart.getUTCDate() - (SERIES_DAYS - 1))

    // Run the aggregates concurrently — they're independent reads.
    const [statusAgg, hostAgg, dayAgg, upcomingCount, hosts] = await Promise.all([
      // Counts per status.
      this.bookingModel
        .aggregate<{ _id: BookingStatus; count: number }>([
          { $match: { tenantId } },
          { $group: { _id: '$status', count: { $sum: 1 } } },
        ])
        .exec(),
      // Counts per host (any status).
      this.bookingModel
        .aggregate<{ _id: Types.ObjectId; count: number }>([
          { $match: { tenantId } },
          { $group: { _id: '$hostId', count: { $sum: 1 } } },
        ])
        .exec(),
      // Bookings created per day over the trailing window (by createdAt).
      this.bookingModel
        .aggregate<{ _id: string; count: number }>([
          { $match: { tenantId, createdAt: { $gte: windowStart } } },
          {
            $group: {
              _id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt', timezone: 'UTC' } },
              count: { $sum: 1 },
            },
          },
        ])
        .exec(),
      // Confirmed bookings still in the future.
      this.bookingModel.countDocuments({ tenantId, status: 'confirmed', startUtc: { $gte: now } }).exec(),
      // Host display names for the breakdown labels.
      this.hostModel.find({ tenantId }).select('_id displayName').exec(),
    ])

    const byStatus: Record<BookingStatus, number> = {
      pending: 0,
      confirmed: 0,
      cancelled: 0,
      rescheduled: 0,
      calendar_failed: 0,
    }
    let totalBookings = 0
    for (const row of statusAgg) {
      if (row._id in byStatus) byStatus[row._id] = row.count
      totalBookings += row.count
    }

    const nameById = new Map(hosts.map((h) => [h._id.toString(), h.displayName]))
    const byHost: HostCount[] = hostAgg
      .map((row) => ({
        hostId: row._id.toString(),
        displayName: nameById.get(row._id.toString()) ?? 'Unknown host',
        count: row.count,
      }))
      .sort((a, b) => b.count - a.count)

    // Fill the day series so every day in the window has a point (zero when no
    // bookings), keeping the front-end chart simple.
    const countByDate = new Map(dayAgg.map((row) => [row._id, row.count]))
    const last30Days: DayPoint[] = []
    for (let i = 0; i < SERIES_DAYS; i++) {
      const d = new Date(windowStart)
      d.setUTCDate(d.getUTCDate() + i)
      const date = d.toISOString().slice(0, 10)
      last30Days.push({ date, count: countByDate.get(date) ?? 0 })
    }

    return { totalBookings, upcomingCount, byStatus, byHost, last30Days }
  }
}
