import crypto from 'crypto';
import Doctor, { IDoctor } from '../models/Doctor';
import Plan from '../models/Plan';
import Subscription from '../models/Subscription';
import User from '../models/User';
import EmailService from './EmailService';

interface PaginationOptions {
  page?: number;
  limit?: number;
  skip?: number;
}

interface PaginatedResponse<T> {
  data: T[];
  pagination: {
    page: number;
    limit: number;
    total: number;
    totalPages: number;
  };
}

interface CreateDoctorWithUserData {
  firstName: string;
  lastName: string;
  email: string;
  phone?: string;
  isDentist?: boolean;
  licenseNumber?: string;
  profileImage?: string;
  planId?: string; // Optional plan to assign to the new doctor
}

export class DoctorService {
  private doctorUserPopulate = {
    path: 'userId',
    populate: {
      path: 'ownerId',
      select: 'firstName lastName email'
    }
  };

  private getDuplicateFieldMessage(error: any): string | null {
    if (error?.code !== 11000 || !error?.keyPattern) return null;
    if (error.keyPattern.licenseNumber) {
      return 'License number already exists. Please use a different license number.';
    }
    if (error.keyPattern.email) {
      return 'Doctor with this email already exists';
    }
    return 'Duplicate value found. Please use different details.';
  }

  async getAllDoctors(limit?: number, page?: number, skip?: number, search?: string): Promise<IDoctor[] | PaginatedResponse<IDoctor>> {
    // If using old pagination style (just limit), maintain backward compatibility
    if (limit && !page && !skip) {
      const query = Doctor.find().populate(this.doctorUserPopulate as any).sort({ createdAt: -1 });
      query.limit(limit);
      return await query.exec();
    }

    // New pagination style with page/limit
    const pageNum = page || 1;
    const limitNum = limit || 10;
    const skipNum = skip || (pageNum - 1) * limitNum;
    const normalizedSearch = (search || '').trim();

    const query: any = {};
    if (normalizedSearch) {
      const regex = new RegExp(normalizedSearch, 'i');
      const matchedUsers = await User.find({
        $or: [
          { firstName: regex },
          { lastName: regex },
          { email: regex },
          { phone: regex }
        ]
      }).select('_id').lean();
      const userIds = matchedUsers.map((u) => u._id);
      const orFilters: any[] = [{ licenseNumber: regex }];
      if (userIds.length > 0) {
        orFilters.push({ userId: { $in: userIds } });
      }
      query.$or = orFilters;
    }

    const [data, total] = await Promise.all([
      Doctor.find(query).populate(this.doctorUserPopulate as any).sort({ createdAt: -1 }).skip(skipNum).limit(limitNum).exec(),
      Doctor.countDocuments(query)
    ]);

    return {
      data,
      pagination: {
        page: pageNum,
        limit: limitNum,
        total,
        totalPages: Math.ceil(total / limitNum)
      }
    };
  }

  async getDoctorById(id: string): Promise<IDoctor | null> {
    return await Doctor.findById(id).populate(this.doctorUserPopulate as any);
  }

  async createDoctor(doctorData: Partial<IDoctor>): Promise<IDoctor> {
    const doctor = new Doctor(doctorData);
    const saved = await doctor.save();
    return await saved.populate(this.doctorUserPopulate as any);
  }

  /**
   * Create a doctor with a new or existing user
   */
  async createDoctorWithUser(data: CreateDoctorWithUserData) {
    const { firstName, lastName, email, phone, isDentist, licenseNumber, profileImage } = data;

    // Validate required fields
    if (!firstName || !lastName || !email) {
      throw new Error('First name, last name, and email are required');
    }

    // Check if user exists
    const normalizedEmail = email.toLowerCase();
    let user = await User.findOne({ email: normalizedEmail });
    let createdNewUser = false;
    const normalizedLicenseNumber = licenseNumber && licenseNumber.trim() !== '' ? licenseNumber.trim() : undefined;

    if (!user) {
      // Create new user
      const password = crypto.randomBytes(8).toString('hex');
      user = await User.create({
        firstName,
        lastName,
        email: normalizedEmail,
        password,
        userType: 'doctor',
        phone,
        isActive: true,
        profileImage
      });
      createdNewUser = true;
    } else {
      // Check if doctor record already exists for this user
      const existingDoctor = await this.getDoctorByUserId(user._id.toString());
      if (existingDoctor) {
        throw new Error('Doctor with this email already exists');
      }
    }

    if (normalizedLicenseNumber) {
      const existingLicense = await Doctor.findOne({ licenseNumber: normalizedLicenseNumber }).select('_id');
      if (existingLicense) {
        throw new Error('License number already exists. Please use a different license number.');
      }
    }

    // Create Doctor Record
    const doctorData = {
      userId: user._id,
      isDentist: isDentist || false,
      licenseNumber: normalizedLicenseNumber,
    };
    let doctor: IDoctor;
    try {
      doctor = await this.createDoctor(doctorData);
    } catch (doctorErr: any) {
      if (createdNewUser) {
        // Roll back user creation to avoid partial insert when doctor creation fails.
        await User.deleteOne({ _id: user._id });
      }
      const duplicateMessage = this.getDuplicateFieldMessage(doctorErr);
      if (duplicateMessage) {
        throw new Error(duplicateMessage);
      }
      throw doctorErr;
    }

    // Send welcome email if new user
    if (createdNewUser) {
      try {
        await EmailService.sendWelcomeEmail(email, firstName, user.language || 'en');
      } catch (emailError: any) {
        console.error('Failed to send welcome email:', emailError);
      }
    }

    // Create subscription if planId provided (for new users or recovering existing users without subscription)
    if (data.planId) {
      try {
        // Only create subscription if user doesn't already have one
        const existingSub = await Subscription.findOne({ userId: user._id });
        if (!existingSub) {
          const plan = await Plan.findById(data.planId);
          if (plan) {
            const isFreeForever = plan.price === 0 || plan.period === 'freely';
            const startDate = new Date();
            const endDate = isFreeForever
              ? new Date('2099-12-31T23:59:59.999Z')
              : new Date(new Date().setFullYear(new Date().getFullYear() + 1));

            await Subscription.create({
              userId: user._id,
              planId: plan._id,
              category: 'Medical',
              startDate,
              endDate,
              status: 'Active',
              autoRenew: false,
            });
          }
        }
      } catch (subErr: any) {
        console.error('Failed to create subscription for new doctor:', subErr);
      }
    }

    return {
      doctor,
      createdNewUser,
      message: createdNewUser ? 'Doctor created and welcome email sent.' : 'Doctor record created for existing user.'
    };
  }

  async updateDoctor(id: string, doctorData: any): Promise<IDoctor | null> {
    const doctor = await Doctor.findById(id);
    if (!doctor) return null;

    // Update User fields
    if (doctorData.firstName || doctorData.lastName || doctorData.phone || doctorData.email) {
      await User.findByIdAndUpdate(doctor.userId, {
        firstName: doctorData.firstName,
        lastName: doctorData.lastName,
        phone: doctorData.phone,
        email: doctorData.email,
      });
    }

    // Update Doctor fields
    return await Doctor.findByIdAndUpdate(id, doctorData, { new: true }).populate(this.doctorUserPopulate as any);
  }

  async deleteDoctor(id: string): Promise<IDoctor | null> {
    return await Doctor.findByIdAndDelete(id);
  }

  async getDoctorByUserId(userId: string): Promise<IDoctor | null> {
    return await Doctor.findOne({ userId }).populate(this.doctorUserPopulate as any);
  }
}

export default new DoctorService();
