import { prisma } from '../config/database';
import { Prisma } from '@prisma/client';
import { config } from '../config';
import { logger } from '../config/logger';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import {
    UserResponse,
    ProfileResponse,
    ProfileUpdate,
    ProfileUpdateResponse,
    ChangePasswordRequest,
    ChangePasswordResponse,
    updateAvatarRequest,
    updateAvatarResponse,
    SendVerifyEmailResponse,
    VerifyEmailResponse
} from '../types/user.types';
import nodemailer from 'nodemailer';
import { UnauthorizedError, ValidationError, ForbiddenError } from '../middleware/errorHandler';
import { uploadFile, deleteFile, getPresignedUrl } from '../config/minio';
import { auditService } from './audit.service';
import { AuditAction } from '@prisma/client';

export class UserService {
    async getUserProfile(token: string): Promise<UserResponse> {
        try {
            // Decode JWT token to get user ID
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string };

            const user = await prisma.user.findUnique({
                where: {
                    id: decoded.id
                },
                include: {
                    profile: true,
                    role: true
                }
            });

            if (!user) throw new UnauthorizedError("User not found");

            // Check if account is deactivated
            if (user.is_deactivated) {
                logger.warn('Profile access attempt with deactivated account', { userId: user.id });
                throw new ForbiddenError('This account has been deactivated');
            }

            return {
                id: user.id,
                username: user.username,
                email: user.email,
                email_verified_at: user.email_verified_at,
                updated_at: user.updated_at,
                created_at: user.created_at,
                role: {
                    code: user.role.code,
                    name: user.role.name as { th: string; en: string }
                },
                profile: user.profile ? {
                    prefix: user.profile.prefix as { th?: string; en?: string } | undefined,
                    first_name: user.profile.first_name,
                    last_name: user.profile.last_name,
                    avatar_url: user.profile.avatar_url ? await this.getAvatarPresignedUrl(user.profile.avatar_url) : null,
                    birth_date: user.profile.birth_date,
                    phone: user.profile.phone
                } : undefined
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) {
                logger.error('Invalid JWT token:', error);
                throw new UnauthorizedError('Invalid token');
            }
            if (error instanceof jwt.TokenExpiredError) {
                logger.error('JWT token expired:', error);
                throw new UnauthorizedError('Token expired');
            }
            logger.error('Error fetching user profile:', error);
            throw error;
        }
    };

    /**
     * Change user password
     */
    async changePassword(token: string, oldPassword: string, newPassword: string): Promise<ChangePasswordResponse> {
        try {
            // Decode JWT token to get user ID
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string };

            const user = await prisma.user.findUnique({ where: { id: decoded.id } });
            if (!user) throw new UnauthorizedError('User not found');

            // Check if account is deactivated
            if (user.is_deactivated) {
                logger.warn('Password change attempt with deactivated account', { userId: user.id });
                throw new ForbiddenError('This account has been deactivated');
            }

            // Verify old password
            const isPasswordValid = await bcrypt.compare(oldPassword, user.password);
            if (!isPasswordValid) throw new UnauthorizedError('Invalid old password');

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

            // Update password
            await prisma.user.update({
                where: { id: user.id },
                data: { password: encryptedPassword }
            });

            logger.info('Password changed successfully', { userId: user.id });

            // Audit log - CHANGE_PASSWORD
            auditService.log({
                userId: user.id,
                action: AuditAction.CHANGE_PASSWORD,
                entityType: 'User',
                entityId: user.id,
                metadata: { email: user.email }
            });

            return {
                code: 200,
                message: 'Password changed successfully'
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) {
                logger.error('Invalid JWT token:', error);
                throw new UnauthorizedError('Invalid token');
            }
            if (error instanceof jwt.TokenExpiredError) {
                logger.error('JWT token expired:', error);
                throw new UnauthorizedError('Token expired');
            }
            logger.error('Failed to change password', { error });
            throw error;
        }
    }

    /**
     * Update user profile
     */
    async updateProfile(token: string, profile: ProfileUpdate): Promise<ProfileUpdateResponse> {
        try {
            // Decode JWT token to get user ID
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number; username: string; email: string; roleCode: string };

            const user = await prisma.user.findUnique({ where: { id: decoded.id } });
            if (!user) throw new UnauthorizedError('User not found');

            // Check if account is deactivated
            if (user.is_deactivated) {
                logger.warn('Profile update attempt with deactivated account', { userId: user.id });
                throw new ForbiddenError('This account has been deactivated');
            }

            // Update profile
            const updatedProfile = await prisma.userProfile.update({
                where: { user_id: user.id },
                data: profile
            });

            logger.info('Profile updated successfully', { userId: user.id });
            return {
                code: 200,
                message: 'Profile updated successfully',
                data: {
                    id: updatedProfile.id,
                    prefix: updatedProfile.prefix as { th?: string; en?: string } | undefined,
                    first_name: updatedProfile.first_name,
                    last_name: updatedProfile.last_name,
                    avatar_url: updatedProfile.avatar_url,
                    phone: updatedProfile.phone,
                    birth_date: updatedProfile.birth_date
                }
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) {
                logger.error('Invalid JWT token:', error);
                throw new UnauthorizedError('Invalid token');
            }
            if (error instanceof jwt.TokenExpiredError) {
                logger.error('JWT token expired:', error);
                throw new UnauthorizedError('Token expired');
            }
            logger.error('Failed to update profile', { error });
            throw error;
        }
    }

    /**
     * Upload avatar picture to MinIO
     */
    async uploadAvatarPicture(token: string, file: Express.Multer.File): Promise<updateAvatarResponse> {
        try {
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number };

            // Check if user exists
            const user = await prisma.user.findUnique({
                where: { id: decoded.id },
                include: { profile: true }
            });

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

            // Check if account is deactivated
            if (user.is_deactivated) {
                throw new ForbiddenError('This account has been deactivated');
            }

            // Generate unique filename
            const timestamp = Date.now();
            const uniqueId = Math.random().toString(36).substring(2, 15);
            const fileName = file.originalname || 'avatar';
            const extension = fileName.split('.').pop() || 'jpg';
            const safeFilename = `${timestamp}-${uniqueId}.${extension}`;
            const filePath = `avatars/${decoded.id}/${safeFilename}`;

            // Delete old avatar if exists
            if (user.profile?.avatar_url) {
                try {
                    await deleteFile(user.profile.avatar_url);
                    logger.info(`Deleted old avatar: ${user.profile.avatar_url}`);
                } catch (error) {
                    logger.warn(`Failed to delete old avatar: ${error}`);
                    // Continue with upload even if delete fails
                }
            }

            // Upload to MinIO
            await uploadFile(filePath, file.buffer, file.mimetype || 'image/jpeg');

            logger.info(`Uploaded avatar: ${filePath}`);
            // Update or create profile - store only file path
            if (user.profile) {
                await prisma.userProfile.update({
                    where: { user_id: decoded.id },
                    data: { avatar_url: filePath }
                });
            } else {
                await prisma.userProfile.create({
                    data: {
                        user_id: decoded.id,
                        avatar_url: filePath,
                        first_name: '',
                        last_name: ''
                    }
                });
            }

            // Generate presigned URL for response
            const presignedUrl = await this.getAvatarPresignedUrl(filePath);

            return {
                code: 200,
                message: 'Avatar uploaded successfully',
                data: {
                    id: decoded.id,
                    avatar_url: presignedUrl
                }
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) {
                logger.error('Invalid JWT token:', error);
                throw new UnauthorizedError('Invalid token');
            }
            if (error instanceof jwt.TokenExpiredError) {
                logger.error('JWT token expired:', error);
                throw new UnauthorizedError('Token expired');
            }
            logger.error('Failed to upload avatar', { error });
            throw error;
        }
    }

    /**
     * Get presigned URL for avatar
     */
    private async getAvatarPresignedUrl(avatarPath: string): Promise<string> {
        try {
            // avatarPath is now stored as file path directly (e.g., avatars/1/filename.jpg)
            return await getPresignedUrl(avatarPath, 3600); // 1 hour expiry
        } catch (error) {
            logger.warn(`Failed to generate presigned URL for avatar: ${error}`);
            throw error;
        }
    }

    /**
         * Format user response
         */
    private formatUserResponse(user: any): UserResponse {
        return {
            id: user.id,
            username: user.username,
            email: user.email,
            email_verified_at: user.email_verified_at,
            updated_at: user.updated_at,
            created_at: user.created_at,
            role: {
                code: user.role.code,
                name: user.role.name
            },
            profile: user.profile ? {
                prefix: user.profile.prefix,
                first_name: user.profile.first_name,
                last_name: user.profile.last_name,
                avatar_url: user.profile.avatar_url,
                phone: user.profile.phone,
                birth_date: user.profile.birth_date
            } : undefined
        };
    }

    /**
     * Send verification email to user
     */
    async sendVerifyEmail(token: string): Promise<SendVerifyEmailResponse> {
        try {
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number; email: string; roleCode: string };

            const user = await prisma.user.findUnique({
                where: { id: decoded.id },
                include: { role: true }
            });

            if (!user) throw new UnauthorizedError('User not found');
            if (user.email_verified_at) throw new ValidationError('Email already verified');

            // Generate verification token (expires in 24 hours)
            const verifyToken = jwt.sign(
                { id: user.id, email: user.email, type: 'email_verify' },
                config.jwt.secret,
                { expiresIn: '24h' }
            );

            // Create verification URL based on role
            const isInstructor = user.role.code === 'INSTRUCTOR';
            const baseUrl = isInstructor 
                ? (process.env.FRONTEND_URL_INSTRUCTOR || 'http://localhost:3001')
                : (process.env.FRONTEND_URL_STUDENT || 'http://localhost:3000');
            const verifyURL = `${baseUrl}/verify-email?token=${verifyToken}`;

            // Create transporter
            const transporter = nodemailer.createTransport({
                host: config.smtp.host,
                port: config.smtp.port,
                secure: config.smtp.secure,
                ...(config.smtp.user && config.smtp.pass && {
                    auth: {
                        user: config.smtp.user,
                        pass: config.smtp.pass
                    }
                })
            });

            // Send email
            const mailOptions = {
                from: config.smtp.from,
                to: user.email,
                subject: 'Email Verification - E-Learning Platform',
                text: `Please verify your email address by clicking on the following link:\n\n${verifyURL}\n\nThis link will expire in 24 hours.\n\nIf you did not create an account, please ignore this email.\n`,
                html: `
                    <h2>Email Verification</h2>
                    <p>Thank you for registering with E-Learning Platform.</p>
                    <p>Please click on the following link to verify your email address:</p>
                    <p><a href="${verifyURL}">${verifyURL}</a></p>
                    <p>This link will expire in 24 hours.</p>
                    <p>If you did not create an account, please ignore this email.</p>
                `
            };

            await transporter.sendMail(mailOptions);

            logger.info('Verification email sent', { email: user.email });
            return {
                code: 200,
                message: 'Verification email sent successfully'
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) throw new UnauthorizedError('Invalid token');
            if (error instanceof jwt.TokenExpiredError) throw new UnauthorizedError('Token expired');
            logger.error('Failed to send verification email', { error });
            throw error;
        }
    }

    /**
     * Verify email with token
     */
    async verifyEmail(verifyToken: string): Promise<VerifyEmailResponse> {
        try {
            const decoded = jwt.verify(verifyToken, config.jwt.secret) as { id: number; email: string; type: string };

            if (decoded.type !== 'email_verify') {
                throw new UnauthorizedError('Invalid verification token');
            }

            const user = await prisma.user.findUnique({
                where: { id: decoded.id }
            });

            if (!user) throw new UnauthorizedError('User not found');

            if (user.email_verified_at) throw new ValidationError('Email already verified');

            // Update email_verified_at
            await prisma.user.update({
                where: { id: user.id },
                data: { email_verified_at: new Date() }
            });

            logger.info('Email verified successfully', { userId: user.id, email: user.email });
            return {
                code: 200,
                message: 'Email verified successfully'
            };
        } catch (error) {
            if (error instanceof jwt.JsonWebTokenError) throw new UnauthorizedError('Invalid verification token');
            if (error instanceof jwt.TokenExpiredError) throw new UnauthorizedError('Verification link has expired');
            logger.error('Failed to verify email', { error });
            throw error;
        }
    }
}
