import { Injectable, ConflictException, Logger, UnauthorizedException, NotFoundException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from '../prisma/prisma.service';
import * as bcrypt from 'bcrypt';
import { CreateUserDto } from '../dto/create-user.dto';
import { ConfigService } from '@nestjs/config';
import { EmailService } from '../email/email.service';
import { randomBytes } from 'crypto';

@Injectable()
export class AuthService {
  private readonly logger = new Logger(AuthService.name);

  constructor(
    private prisma: PrismaService,
    private jwtService: JwtService,
    private configService: ConfigService,
    private emailService: EmailService,
  ) {
    this.logger.log('AuthService initialized with EmailService');
  }

  async validateUser(email: string, password: string): Promise<any> {
    this.logger.debug('Validating user:', { email });
    
    try {
      const user = await this.prisma.user.findUnique({
        where: { email },
        select: {
          id: true,
          email: true,
          name: true,
          password: true,
          isAdmin: true,
        },
      });

      this.logger.debug('Database query result:', {
        userFound: !!user,
        userData: user ? {
          id: user.id,
          email: user.email,
          name: user.name,
          isAdmin: user.isAdmin,
        } : null,
      });

      if (!user) {
        this.logger.debug('User not found:', { email });
        return null;
      }

      const isPasswordValid = await bcrypt.compare(password, user.password);
      this.logger.debug('Password validation result:', { isPasswordValid });
      
      if (!isPasswordValid) {
        this.logger.debug('Invalid password for user:', { email });
        return null;
      }

      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const { password: _, ...result } = user;
      this.logger.debug('User validated successfully:', { 
        id: result.id,
        email: result.email,
        name: result.name,
        isAdmin: result.isAdmin,
      });
      
      return result;
    } catch (error) {
      this.logger.error('Error validating user:', {
        error: error.message,
        stack: error.stack,
      });
      throw error;
    }
  }

  async login(user: any) {
    this.logger.debug('Login called with user:', { 
      id: user.id,
      email: user.email,
      name: user.name,
      isAdmin: user.isAdmin,
    });

    try {
      const payload = { 
        email: user.email,
        sub: user.id,
      };
      
      this.logger.debug('Generated JWT payload:', payload);
      
      const token = this.jwtService.sign(payload, {
        secret: this.configService.get<string>('JWT_SECRET'),
      });

      this.logger.debug('JWT token generated successfully');
      
      return {
        access_token: token,
        user: {
          id: user.id,
          email: user.email,
          name: user.name,
          isAdmin: user.isAdmin,
        },
      };
    } catch (error) {
      this.logger.error('Error generating JWT token:', {
        error: error.message,
        stack: error.stack,
      });
      throw error;
    }
  }

  async createUser(
    createUserDto: CreateUserDto,
    isAdmin: boolean = false,
  ): Promise<any> {
    console.log('=== Starting user creation process ===');
    this.logger.log('=== Starting user creation process ===');
    console.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' });
    this.logger.log('Creating user:', { ...createUserDto, isAdmin, password: '[REDACTED]' });

    try {
      // Check for existing user
      console.log('Checking for existing user...');
      this.logger.log('Checking for existing user...');
      const existingUser = await this.prisma.user.findUnique({
        where: { email: createUserDto.email },
      });

      if (existingUser) {
        console.log('User already exists:', createUserDto.email);
        this.logger.warn('User already exists:', createUserDto.email);
        throw new ConflictException('Email already exists');
      }

      // Hash password
      console.log('Hashing password...');
      this.logger.log('Hashing password...');
      const hashedPassword = await bcrypt.hash(createUserDto.password, 10);

      // Create user
      console.log('Creating user in database...');
      this.logger.log('Creating user in database...');
      const newUser = await this.prisma.user.create({
        data: {
          email: createUserDto.email,
          password: hashedPassword,
          name: createUserDto.name,
          isAdmin: isAdmin,
        },
      });

      console.log('User created successfully:', { id: newUser.id, email: newUser.email });
      this.logger.log('User created successfully:', { id: newUser.id, email: newUser.email });

      // Send welcome email
      console.log('Attempting to send welcome email...');
      this.logger.log('Attempting to send welcome email...');
      
      try {
        console.log('Calling EmailService.sendWelcomeEmail...');
        this.logger.log('Calling EmailService.sendWelcomeEmail...');
        await this.emailService.sendWelcomeEmail(newUser.email, newUser.name);
        console.log('Welcome email sent successfully');
        this.logger.log('Welcome email sent successfully');
      } catch (emailError) {
        console.error('Failed to send welcome email:', emailError);
        this.logger.error('Failed to send welcome email:', {
          error: emailError.message,
          code: emailError.code,
          command: emailError.command,
          response: emailError.response,
          stack: emailError.stack,
        });
        // Don't throw the error, just log it
      }

      // Return user data
      const { password, ...result } = newUser;
      console.log('=== User creation completed ===');
      this.logger.log('=== User creation completed ===');
      return result;
    } catch (error) {
      console.error('Error in createUser:', error);
      this.logger.error('Error in createUser:', {
        error: error.message,
        code: error.code,
        command: error.command,
        stack: error.stack,
      });
      throw error;
    }
  }

  // async getUserInfo(userId: number) {
  //   return this.prisma.user.findUnique({
  //     where: { id: userId },
  //     select: {
  //       id: true,
  //       name: true,
  //       email: true,
  //       isAdmin: true,
  //     },
  //   });
  // }
  async getUserInfo(userId: number) {
    if (!userId) {
      throw new Error('User ID is required');
    }
    
    return this.prisma.user.findUnique({
      where: {
        id: userId,  // Make sure userId is properly passed and converted to number if needed
      },
      select: {
        id: true,
        name: true,
        email: true,
        isAdmin: true
      }
    });
  }

  async sendPasswordResetToken(email: string) {
    this.logger.log('=== Starting password reset token process ===');
    this.logger.debug('Reset token requested for:', email);

    try {
      // Find the user
      const user = await this.prisma.user.findUnique({
        where: { email },
        select: {
          id: true,
          email: true,
          name: true,
        },
      });

      if (!user) {
        this.logger.warn('User not found for password reset:', email);
        // Return success anyway to prevent email enumeration
        return { message: 'If an account exists, a password reset link has been sent' };
      }

      // Generate reset token
      const token = randomBytes(32).toString('hex');
      const expiresAt = new Date();
      expiresAt.setHours(expiresAt.getHours() + 1); // Token expires in 1 hour

      // Save reset token
      await this.prisma.passwordReset.create({
        data: {
          token,
          userId: user.id,
          expiresAt,
        },
      });

      // Send email with reset link
      try {
        await this.emailService.sendPasswordResetEmail(
          user.email,
          user.name,
          token
        );
        this.logger.debug('Password reset email sent successfully');
      } catch (emailError) {
        this.logger.error('Failed to send password reset email:', {
          error: emailError.message,
          code: emailError.code,
          stack: emailError.stack,
        });
        throw emailError;
      }

      return { message: 'If an account exists, a password reset link has been sent' };
    } catch (error) {
      this.logger.error('Error in sendPasswordResetToken:', {
        error: error.message,
        code: error.code,
        stack: error.stack,
      });
      throw error;
    }
  }

  async resetPasswordWithToken(token: string, newPassword: string) {
    this.logger.log('=== Starting password reset with token process ===');
    this.logger.debug('Password reset attempted with token');

    try {
      // Find valid reset token
      const resetRecord = await this.prisma.passwordReset.findFirst({
        where: {
          token,
          expiresAt: {
            gt: new Date(), // Token must not be expired
          },
          used: false, // Token must not be used
        },
        include: {
          user: true,
        },
      });

      if (!resetRecord) {
        this.logger.warn('Invalid or expired reset token used');
        throw new UnauthorizedException('Invalid or expired reset token');
      }

      // Hash new password
      const hashedPassword = await bcrypt.hash(newPassword, 10);

      // Update password and mark token as used
      await this.prisma.$transaction([
        this.prisma.user.update({
          where: { id: resetRecord.userId },
          data: { password: hashedPassword },
        }),
        this.prisma.passwordReset.update({
          where: { id: resetRecord.id },
          data: { used: true },
        }),
      ]);

      this.logger.log('Password reset successful for user:', resetRecord.userId);

      // Send confirmation email
      try {
        await this.emailService.sendPasswordChangeConfirmation(
          resetRecord.user.email,
          resetRecord.user.name
        );
        this.logger.debug('Password change confirmation email sent');
      } catch (emailError) {
        this.logger.error('Failed to send password change confirmation:', {
          error: emailError.message,
          code: emailError.code,
          stack: emailError.stack,
        });
        // Don't throw error here as password is already changed
      }

      return { message: 'Password reset successful' };
    } catch (error) {
      this.logger.error('Error in resetPasswordWithToken:', {
        error: error.message,
        code: error.code,
        stack: error.stack,
      });
      throw error;
    }
  }
}
