import jwt from 'jsonwebtoken';
import Doctor from '../models/Doctor';
import Plan from '../models/Plan';
import Subscription from '../models/Subscription';
import User from '../models/User';
import EmailService from './EmailService';

interface RegisterData {
  firstName: string;
  lastName: string;
  email: string;
  password: string;
  confirmPassword: string;
  userType?: 'doctor' | 'patient' | 'admin' | 'staff' | 'dentist';
  isDentist?: boolean;
  language?: 'es' | 'en';
}

interface LoginData {
  email: string;
  password: string;
}

interface ResetPasswordData {
  token: string;
  newPassword: string;
  confirmPassword: string;
}

interface VerifyTokenData {
  token: string;
}

interface UpdateProfileData {
  token: string;
  firstName?: string;
  lastName?: string;
  phone?: string;
  profileImage?: string;
  language?: 'es' | 'en';
  specialization?: string;
  licenseNumber?: string;
  clinic?: {
    name?: string;
    address?: string;
    phone?: string;
  };
}

interface ChangePasswordData {
  token: string;
  currentPassword: string;
  newPassword: string;
  confirmPassword: string;
}

export class AuthService {
  private jwtSecret = process.env.JWT_SECRET || 'your-secret-key';

  /**
   * Register a new user with free trial subscription
   */
  async register(data: RegisterData) {
    const { firstName, lastName, email, password, confirmPassword, userType = 'patient', language } = data;

    // Validate input
    if (!firstName || !lastName || !email || !password || !confirmPassword) {
      throw new Error('Please provide all required fields');
    }

    if (password !== confirmPassword) {
      throw new Error('Passwords do not match');
    }

    if (password.length < 6) {
      throw new Error('Password must be at least 6 characters long');
    }

    // Check if user already exists
    const normalizedEmail = email.toLowerCase();
    const existingUser = await User.findOne({ email: normalizedEmail });
    if (existingUser) {
      throw new Error('Email already registered');
    }

    // Create user
    const user = new User({
      firstName,
      lastName,
      email: normalizedEmail,
      password,
      userType,
      isDentist: userType === 'dentist' ? true : (userType === 'doctor' ? data.isDentist : false),
      language: language === 'es' || language === 'en' ? language : 'en',
    });

    await user.save();

    // Create free trial subscription (48 hours)
    try {
      // Look for a 'trial' plan specifically — distinct from admin 'Free Forever' (period:'freely')
      let freeTrialPlan = await Plan.findOne({ period: 'trial' });

      if (!freeTrialPlan) {
        freeTrialPlan = await Plan.create({
          title: 'Free Trial',
          price: 0,
          period: 'trial',
          features: [
            '48-hour trial period',
            'Access to basic features',
            'Limited appointments',
            'Email support'
          ]
        });
      }

      const startDate = new Date();
      const endDate = new Date(startDate.getTime() + 48 * 60 * 60 * 1000); // exactly 48 hours

      const subscription = await Subscription.create({
        userId: user._id,
        planId: freeTrialPlan._id,
        category: 'Medical',
        startDate,
        endDate,
        status: 'Active',
        autoRenew: false
      });

      if (subscription) {
        console.log(`Free trial subscription created for user ${user.email}`);

        // Send subscription confirmation email
        try {
          await EmailService.sendSubscriptionConfirmationEmail(
            user.email,
            user.firstName,
            freeTrialPlan.title || 'Free Trial',
            user.language || 'en'
          );
          console.log(`Subscription confirmation email sent to ${user.email}`);
        } catch (emailError: any) {
          console.error('Failed to send subscription confirmation email:', emailError);
        }
      }
    } catch (subError: any) {
      console.error('Error creating free trial subscription:', subError);
    }

    // Create Doctor record if needed - this MUST succeed for doctor/dentist users
    if (userType === 'doctor' || userType === 'dentist') {
      try {
        await Doctor.create({
          userId: user._id,
          isDentist: userType === 'dentist' || data.isDentist || false,
          createdAt: new Date()
        });
        console.log(`Doctor record created for user ${user.email} (Type: ${userType})`);
      } catch (doctorError: any) {
        console.error('Error creating doctor record:', doctorError);
        // Clean up the user since the doctor record failed
        await User.findByIdAndDelete(user._id);
        throw new Error('Registration failed: could not create doctor profile. Please try again.');
      }
    }

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

    return {
      id: user._id,
      firstName: user.firstName,
      lastName: user.lastName,
      email: user.email,
      userType: user.userType,
    };
  }

  /**
   * Login user and return JWT token
   */
  async login(data: LoginData) {
    const { email, password } = data;

    if (!email || !password) {
      throw new Error('Please provide email and password');
    }

    const normalizedEmail = email.toLowerCase();
    const user = await User.findOne({ email: normalizedEmail }).select('+password');

    if (!user) {
      throw new Error('Email incorrect');
    }

    if (!user.password) {
      throw new Error('Please complete your subscription to receive your login credentials');
    }

    const isPasswordValid = await user.comparePassword(password);
    if (!isPasswordValid) {
      throw new Error('Password incorrect');
    }

    // Update last login
    user.lastLogin = new Date();
    await user.save();

    // Check for active subscription
    const subscriptionUserId = user.ownerId || user._id;
    const activeSubscription = await Subscription.findOne({
      userId: subscriptionUserId,
      status: 'Active',
      endDate: { $gt: new Date() }
    }).populate('planId');

    // If secondary user and owner has no active plan, block login
    if (user.ownerId && !activeSubscription) {
      throw new Error('Access denied. The primary account subscription is inactive or expired.');
    }

    let subscriptionDetails: any = {
      subscriptionStatus: null,
      subscriptionPlan: null,
      subscriptionExpires: null,
    };

    if (activeSubscription) {
      const plan = activeSubscription.planId as any;
      subscriptionDetails = {
        subscriptionStatus: activeSubscription.status,
        subscriptionPlan: plan?.title || 'Unknown',
        subscriptionExpires: activeSubscription.endDate,
      };
    }

    // Generate JWT token
    const tokenPayload = {
      userId: user._id,
      email: user.email,
      role: user.userType,
      ...subscriptionDetails
    };

    const token = jwt.sign(
      tokenPayload,
      this.jwtSecret,
      { expiresIn: '7d' }
    );

    // Look up doctor profile to get the correct isDentist value
    let isDentist = user.isDentist || false;
    if (user.userType === 'doctor' || user.userType === 'dentist') {
      const doctor = await Doctor.findOne({ userId: user._id });
      if (doctor) {
        isDentist = doctor.isDentist;
      }
    }

    return {
      token,
      hasActiveSubscription: !!activeSubscription,
      user: {
        id: user._id,
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email,
        userType: user.userType,
        profileImage: user.profileImage,
        ownerId: user.ownerId,
        isDentist,
      },
    };
  }

  /**
   * Send password reset email
   */
  async forgotPassword(email: string, language?: 'es' | 'en') {
    if (!email) {
      throw new Error('Please provide your email');
    }

    const normalizedEmail = email.toLowerCase();
    const user = await User.findOne({ email: normalizedEmail });

    if (user) {
      const resetToken = user.generatePasswordResetToken();
      await user.save();

      const resetUrl = `${process.env.FRONTEND_URL}/reset-password?token=${resetToken}`;
      const preferredLanguage = language === 'es' || language === 'en'
        ? language
        : (user.language || 'en');

      try {
        await EmailService.sendPasswordResetEmail(user.email, resetUrl, preferredLanguage);
      } catch (emailError: any) {
        console.error('Error sending password reset email:', emailError);
      }
    }

    // Always return success for security
    return { message: 'If an account exists with this email, you will receive a password reset link' };
  }

  /**
   * Reset password with token
   */
  async resetPassword(data: ResetPasswordData) {
    const { token, newPassword, confirmPassword } = data;

    if (!token || !newPassword) {
      throw new Error('Please provide reset token and new password');
    }

    if (newPassword !== confirmPassword) {
      throw new Error('Passwords do not match');
    }

    if (newPassword.length < 6) {
      throw new Error('Password must be at least 6 characters long');
    }

    const user = await User.findOne({
      resetPasswordToken: token,
      resetPasswordExpire: { $gt: new Date() },
    }).select('+resetPasswordToken +resetPasswordExpire');

    if (!user) {
      throw new Error('Invalid or expired reset token');
    }

    user.password = newPassword;
    user.resetPasswordToken = undefined;
    user.resetPasswordExpire = undefined;
    await user.save();

    return { message: 'Password has been reset successfully' };
  }

  /**
   * Verify JWT token
   */
  async verifyToken(data: VerifyTokenData) {
    const { token } = data;

    if (!token) {
      throw new Error('Token is required');
    }

    try {
      const decoded = jwt.verify(token, this.jwtSecret) as any;
      const user = await User.findById(decoded.userId);

      if (!user) {
        throw new Error('User not found');
      }

      const activeSubscription = await Subscription.findOne({
        userId: user._id,
        status: 'Active',
        endDate: { $gt: new Date() }
      });

      return {
        hasActiveSubscription: !!activeSubscription,
        user: {
          id: user._id,
          firstName: user.firstName,
          lastName: user.lastName,
          email: user.email,
          userType: user.userType,
          profileImage: user.profileImage,
          language: user.language,
        },
      };
    } catch (error: any) {
      throw new Error('Invalid or expired token');
    }
  }

  /**
   * Get user profile
   */
  async getProfile(token: string) {
    if (!token) {
      throw new Error('Token is required');
    }

    try {
      const decoded = jwt.verify(token, this.jwtSecret) as any;
      const user = await User.findById(decoded.userId);

      if (!user) {
        throw new Error('User not found');
      }

      const activeSubscription = await Subscription.findOne({
        userId: user._id,
        status: 'Active',
        endDate: { $gt: new Date() }
      });

      const responseUser: any = {
        id: user._id,
        firstName: user.firstName,
        lastName: user.lastName,
        email: user.email,
        phone: user.phone,
        userType: user.userType,
        profileImage: user.profileImage,
        language: user.language,
        isActive: user.isActive,
        createdAt: user.createdAt,
        lastLogin: user.lastLogin,
        ownerId: user.ownerId,
        clinic: user.clinic,
      };

      // If user is a doctor or dentist, include doctor data
      if (user.userType === 'doctor' || user.userType === 'dentist') {
        const doctor = await Doctor.findOne({ userId: user._id });
        if (doctor) {
          responseUser.doctor = {
            signatureUrl: doctor.signatureUrl,
            isDentist: doctor.isDentist,
            licenseNumber: doctor.licenseNumber,
            specialization: doctor.specialization,
            logoUrl: doctor.logoUrl,
          };
        }
      }

      return {
        hasActiveSubscription: !!activeSubscription,
        user: responseUser,
      };
    } catch (error: any) {
      throw new Error('Invalid or expired token');
    }
  }

  /**
   * Update user profile
   */
  async updateProfile(data: UpdateProfileData) {
    const { token, firstName, lastName, phone, profileImage, language, specialization, licenseNumber, clinic } = data;

    if (!token) {
      throw new Error('Token is required');
    }

    try {
      const decoded = jwt.verify(token, this.jwtSecret) as any;
      const user = await User.findById(decoded.userId);

      if (!user) {
        throw new Error('User not found');
      }

      if (firstName) user.firstName = firstName;
      if (lastName) user.lastName = lastName;
      if (phone) user.phone = phone;
      if (profileImage) user.profileImage = profileImage;
      if (language === 'es' || language === 'en') user.language = language;

      if (clinic) {
        user.clinic = {
          ...user.clinic,
          ...clinic
        };
      }

      await user.save();

      // Update doctor-specific fields if user is a doctor/dentist
      let doctorData: any = undefined;
      if (user.userType === 'doctor' || user.userType === 'dentist') {
        const doctorUpdate: any = {};
        if (specialization !== undefined) doctorUpdate.specialization = specialization;
        if (licenseNumber !== undefined) doctorUpdate.licenseNumber = licenseNumber;

        if (Object.keys(doctorUpdate).length > 0) {
          const doctor = await Doctor.findOneAndUpdate(
            { userId: user._id },
            { $set: doctorUpdate },
            { new: true }
          );
          if (doctor) {
            doctorData = {
              signatureUrl: doctor.signatureUrl,
              isDentist: doctor.isDentist,
              licenseNumber: doctor.licenseNumber,
              specialization: doctor.specialization,
              logoUrl: doctor.logoUrl,
            };
          }
        }
      }

      return {
        message: 'Profile updated successfully',
        user: {
          id: user._id,
          firstName: user.firstName,
          lastName: user.lastName,
          email: user.email,
          phone: user.phone,
          userType: user.userType,
          profileImage: user.profileImage,
          language: user.language,
          clinic: user.clinic,
          ...(doctorData ? { doctor: doctorData } : {}),
        },
      };
    } catch (error: any) {
      throw new Error('Invalid or expired token');
    }
  }

  /**
   * Change password
   */
  async changePassword(data: ChangePasswordData) {
    const { token, currentPassword, newPassword, confirmPassword } = data;

    if (!token) {
      throw new Error('Token is required');
    }

    if (!currentPassword || !newPassword || !confirmPassword) {
      throw new Error('Please provide all password fields');
    }

    if (newPassword !== confirmPassword) {
      throw new Error('New passwords do not match');
    }

    if (newPassword.length < 6) {
      throw new Error('New password must be at least 6 characters long');
    }

    try {
      const decoded = jwt.verify(token, this.jwtSecret) as any;
      const user = await User.findById(decoded.userId).select('+password');

      if (!user) {
        throw new Error('User not found');
      }

      const isPasswordValid = await user.comparePassword(currentPassword);
      if (!isPasswordValid) {
        throw new Error('Current password is incorrect');
      }

      user.password = newPassword;
      await user.save();

      return { message: 'Password changed successfully' };
    } catch (error: any) {
      throw new Error('Invalid or expired token');
    }
  }
}

export default new AuthService();
