import mongoose from 'mongoose';
import Appointment from '../models/Appointment';
import Doctor from '../models/Doctor';
import Patient from '../models/Patient';
import Prescription from '../models/Prescription';

export class DashboardService {
  /**
   * Convert string ID to ObjectId
   */
  private toObjectId(id: string | mongoose.Types.ObjectId): mongoose.Types.ObjectId {
    if (id instanceof mongoose.Types.ObjectId) {
      return id;
    }
    if (!mongoose.Types.ObjectId.isValid(id)) {
      throw new Error('Invalid ObjectId');
    }
    return new mongoose.Types.ObjectId(id);
  }

  /**
   * Get doctor ID from user ID
   */
  private async getDoctorIdFromUserId(userId: string): Promise<string | null> {
    const doctor = await Doctor.findOne({ userId });
    return doctor ? doctor._id.toString() : null;
  }

  /**
   * Get dashboard statistics
   */
  async getStats(userId?: string) {
    // Get current month date range
    const startOfMonth = new Date();
    startOfMonth.setDate(1);
    startOfMonth.setHours(0, 0, 0, 0);

    const endOfMonth = new Date(startOfMonth);
    endOfMonth.setMonth(endOfMonth.getMonth() + 1);

    // Check if user is a doctor
    let doctorId: string | null = null;
    if (userId) {
      doctorId = await this.getDoctorIdFromUserId(userId);
    }

    // Build query filters based on user type
    const patientQuery = doctorId ? { doctorId: this.toObjectId(doctorId) } : {};
    const appointmentQuery = doctorId ? {
      appointmentDate: {
        $gte: startOfMonth,
        $lt: endOfMonth
      },
      doctorId: this.toObjectId(doctorId)
    } : {
      appointmentDate: {
        $gte: startOfMonth,
        $lt: endOfMonth
      }
    };

    const prescriptionQuery = doctorId ? { doctorId: this.toObjectId(doctorId) } : {};

    // Fetch all stats in parallel
    const [
      totalPatients,
      appointmentsThisMonth,
      totalPrescriptions,
      totalDoctors,
      newPatientsThisMonth,
      newPatientsLastMonth
    ] = await Promise.all([
      Patient.countDocuments(patientQuery),
      Appointment.countDocuments(appointmentQuery),
      Prescription.countDocuments(prescriptionQuery),
      Doctor.countDocuments(),
      Patient.countDocuments({
        ...patientQuery,
        createdAt: {
          $gte: startOfMonth,
          $lt: endOfMonth
        }
      }),
      this.getLastMonthPatientCount(startOfMonth, doctorId)
    ]);

    // Calculate growth percentage
    let growth = 0;
    if (newPatientsLastMonth === 0) {
      growth = newPatientsThisMonth > 0 ? 100 : 0;
    } else {
      growth = ((newPatientsThisMonth - newPatientsLastMonth) / newPatientsLastMonth) * 100;
    }

    return {
      patients: totalPatients,
      appointments: appointmentsThisMonth,
      prescriptions: totalPrescriptions,
      doctors: totalDoctors,
      growth: parseFloat(growth.toFixed(1))
    };
  }

  /**
   * Get patient count from last month
   */
  private async getLastMonthPatientCount(startOfMonth: Date, doctorId?: string | null) {
    const startOfLastMonth = new Date(startOfMonth);
    startOfLastMonth.setMonth(startOfLastMonth.getMonth() - 1);

    const endOfLastMonth = new Date(startOfMonth);

    const query = doctorId ? {
      doctorId: this.toObjectId(doctorId),
      createdAt: {
        $gte: startOfLastMonth,
        $lt: endOfLastMonth
      }
    } : {
      createdAt: {
        $gte: startOfLastMonth,
        $lt: endOfLastMonth
      }
    };

    const count = await Patient.countDocuments(query);

    return count;
  }
}

export default new DashboardService();
