import crypto from 'crypto';
import Doctor from '../models/Doctor';
import Subscription from '../models/Subscription';
import User from '../models/User';
import EmailService from './EmailService';

interface AddDoctorData {
    firstName: string;
    lastName: string;
    email: string;
    isDentist?: boolean;
    licenseNumber: string;
    password?: string;
    specialization?: string;
}

interface TeamMemberData {
    _id?: string;
    doctorId?: string | null;
    firstName: string;
    lastName: string;
    email: string;
    userType: string;
    isDentist: boolean;
    licenseNumber: string;
    specialization: string;
    isOwner: boolean;
    [key: string]: any; // Allow additional fields from document
}

class DoctorManagerService {
    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 'User already exists';
        }
        return 'Duplicate value found. Please use different details.';
    }

    private getDoctorLimitFromPlan(plan: any): number {
        if (!plan) return 0;

        const period = String(plan.period || '').toLowerCase();
        const title = String(plan.title || '').toLowerCase();

        const isAnnualPlan =
            period === 'annual' ||
            period === '12-month' ||
            title.includes('annual') ||
            title.includes('yearly') ||
            title.includes('12 month');

        if (isAnnualPlan) return 5;

        const isSixMonthPlan =
            period === '6-month' ||
            title.includes('6 month');

        if (isSixMonthPlan) return 2;

        return 0;
    }

    private generateStrongPassword(length = 14): string {
        // Avoid visually ambiguous characters to reduce copy/login mistakes from emails.
        const upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ'; // no I, O
        const lower = 'abcdefghjkmnpqrstuvwxyz'; // no i, l, o
        const digits = '23456789'; // no 0, 1
        const all = upper + lower + digits;

        const pick = (source: string) => source[crypto.randomInt(0, source.length)];

        const required = [
            pick(upper),
            pick(lower),
            pick(digits),
        ];

        while (required.length < length) {
            required.push(pick(all));
        }

        // Fisher-Yates shuffle
        for (let i = required.length - 1; i > 0; i--) {
            const j = crypto.randomInt(0, i + 1);
            [required[i], required[j]] = [required[j], required[i]];
        }

        return required.join('');
    }

    /**
     * Get team members for an owner/doctor account
     */
    async getTeam(userId: string): Promise<any[]> {
        try {
            // Check if user is an owner or a managed account
            const user = await User.findById(userId);
            if (!user) {
                throw new Error('User not found');
            }

            const ownerId = user.ownerId || user._id;

            const teamMembers = await User.find({
                $or: [
                    { _id: ownerId },
                    { ownerId: ownerId }
                ]
            }).select('-password');

            // Fetch doctor profiles for these users
            const doctorProfiles = await Doctor.find({
                userId: { $in: teamMembers.map(u => u._id) }
            });

            // Merge data
            const team = teamMembers.map(member => {
                const profile = doctorProfiles.find(p => p.userId.toString() === member._id.toString());
                return {
                    ...member.toObject(),
                    doctorId: profile?._id?.toString() || null,
                    isDentist: profile?.isDentist || false,
                    licenseNumber: profile?.licenseNumber || 'N/A',
                    specialization: profile?.specialization || 'N/A',
                    isOwner: !member.ownerId
                };
            });

            return team;
        } catch (error: any) {
            console.error('Error fetching team:', error);
            throw new Error(`Failed to fetch team: ${error.message}`);
        }
    }

    /**
     * Add a new doctor under owner account
     */
    async addDoctor(userId: string, data: AddDoctorData): Promise<{ doctor: any; message: string }> {
        try {
            // 1. Verify requester is the owner
            const owner = await User.findById(userId);
            if (!owner) {
                throw new Error('User not found');
            }
            if (owner.ownerId) {
                throw new Error('Only the account owner can add doctors');
            }

            // 2. Check Subscription Limits
            const subscription = await Subscription.findOne({
                userId: owner._id,
                status: 'Active',
                endDate: { $gt: new Date() }
            }).sort({ endDate: -1 }).populate('planId');

            const plan = subscription?.planId as any;
            const maxDoctors = this.getDoctorLimitFromPlan(plan);

            // Count existing doctors under this owner (children ONLY)
            const currentDoctorsCount = await User.countDocuments({
                ownerId: owner._id,
                userType: 'doctor',
                isActive: true
            });

            if (currentDoctorsCount >= maxDoctors) {
                const isMaxPlan = maxDoctors >= 5;
                const errorMsg = isMaxPlan
                    ? 'Maximum limit of doctors reached for this plan'
                    : 'Doctor limit reached. Please upgrade your plan to add more doctors';
                throw new Error(errorMsg);
            }

            // 3. Create User
            const normalizedEmail = data.email.toLowerCase();
            let user = await User.findOne({ email: normalizedEmail });
            let createdNewUser = false;

            const normalizedLicenseNumber =
                data.licenseNumber && data.licenseNumber.trim() !== ''
                    ? data.licenseNumber.trim()
                    : undefined;

            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.');
                }
            }

            const generatedPassword = this.generateStrongPassword();

            if (!user) {
                user = new User({
                    firstName: data.firstName,
                    lastName: data.lastName,
                    email: normalizedEmail,
                    password: generatedPassword,
                    userType: 'doctor',
                    ownerId: owner._id,
                    isActive: true,
                    language: owner.language || 'en'
                });
                await user.save();
                createdNewUser = true;
            } else {
                // Recovery path for orphan teammate users created by older failed flows.
                const existingDoctorProfile = await Doctor.findOne({ userId: user._id }).select('_id');
                const belongsToOwner = user.ownerId?.toString() === owner._id.toString();
                if (existingDoctorProfile || !belongsToOwner) {
                    throw new Error('User already exists');
                }

                user.firstName = data.firstName;
                user.lastName = data.lastName;
                user.userType = 'doctor';
                user.ownerId = owner._id;
                user.isActive = true;
                user.language = owner.language || 'en';
                user.password = generatedPassword;
                await user.save();
            }

            // 4. Create Doctor Profile
            try {
                const doctor = new Doctor({
                    userId: user._id,
                    isDentist: data.isDentist || false,
                    licenseNumber: normalizedLicenseNumber,
                    specialization: data.specialization
                });

                await doctor.save();
            } catch (doctorErr: any) {
                // Roll back created user to avoid partial records when doctor profile fails.
                if (createdNewUser) {
                    await User.deleteOne({ _id: user._id });
                }
                const duplicateMessage = this.getDuplicateFieldMessage(doctorErr);
                if (duplicateMessage) {
                    throw new Error(duplicateMessage);
                }
                throw doctorErr;
            }

            // 5. Send Credentials Email
            try {
                await EmailService.sendNewDoctorCredentials(
                    normalizedEmail,
                    data.firstName,
                    normalizedEmail,
                    generatedPassword,
                    `${owner.firstName} ${owner.lastName}`.trim(),
                    user.language || 'en'
                );
            } catch (emailErr) {
                console.error('Failed to send credential email:', emailErr);
                // Do not fail the request if email fails, but log it
            }

            return {
                doctor: user,
                message: 'Doctor added successfully'
            };
        } catch (error: any) {
            console.error('Error adding doctor:', error);
            const duplicateMessage = this.getDuplicateFieldMessage(error);
            if (duplicateMessage) {
                throw new Error(duplicateMessage);
            }
            throw new Error(error.message || `Failed to add doctor: ${error.message}`);
        }
    }

    /**
     * Remove a doctor from owner's team
     */
    async removeDoctor(userId: string, doctorUserId: string): Promise<{ message: string }> {
        try {
            const owner = await User.findById(userId);
            if (!owner) {
                throw new Error('User not found');
            }

            if (owner.ownerId) {
                throw new Error('Only the account owner can delete doctors');
            }

            if (!doctorUserId) {
                throw new Error('Doctor ID is required');
            }

            if (owner._id.toString() === doctorUserId) {
                throw new Error('Primary doctor cannot be deleted');
            }

            const doctorUser = await User.findOne({
                _id: doctorUserId,
                ownerId: owner._id,
                userType: 'doctor'
            });

            if (!doctorUser) {
                throw new Error('Doctor not found in your team');
            }

            await Doctor.deleteOne({ userId: doctorUser._id });
            await User.deleteOne({ _id: doctorUser._id });

            return {
                message: 'Doctor deleted successfully'
            };
        } catch (error: any) {
            console.error('Error deleting team doctor:', error);
            throw new Error(error.message || `Failed to delete doctor: ${error.message}`);
        }
    }

    /**
     * Update subscription limit (mock upgrade for testing)
     */
    async upgradeLimit(userId: string, newLimit: number): Promise<{ message: string }> {
        try {
            const subscription = await Subscription.findOne({ userId, status: 'Active' });
            if (!subscription) {
                throw new Error('No active subscription found');
            }

            (subscription as any).maxDoctors = newLimit;
            await subscription.save();

            return {
                message: `Plan upgraded. Max doctors: ${newLimit}`
            };
        } catch (error: any) {
            console.error('Error upgrading limit:', error);
            throw new Error(error.message || `Failed to upgrade limit: ${error.message}`);
        }
    }
}

export default new DoctorManagerService();
