import { RequestQuery, Request, ResponseToolkit, ResponseObject } from '@hapi/hapi'
import { UserService } from "../services/user.service"
import { AttachmentService } from "../services/attachment.service"
import { SessionService } from "../services/session.service"
import { AppError } from "../../utils/errors"
import { Common } from "../../utils/common"
import * as csv from 'fast-csv';
import { PassThrough } from 'stream';
import { parsePhoneNumberFromString } from 'libphonenumber-js';
import Models from '../models'
import { USER } from '../config/constants'

export class UserHandler {
    // set the class variables
    private getUserServiceObject = async (request: Request) => {
        let variables = await Common.getVariables(request)
        let userService = new UserService(variables);
        return userService;
    }

    // set the class variables
    private getAttachmentServiceObject = async (request: Request) => {
        let variables = await Common.getVariables(request)
        let attachmentService = new AttachmentService(variables);
        return attachmentService;
    }

    private getForwardHeaders = (request: Request): Record<string, string> => {
        const headers: Record<string, string> = {};
        const requiredHeaders = ['accept', 'language', 'timezone', 'account', 'authorization'];
        for (const header of requiredHeaders) {
            const value = request.headers[header];
            if (typeof value === 'string' && value) headers[header] = value;
        }
        return headers;
    }

    private buildFlowResponse = (steps: UserFlowStepObject[], entityId: number | null) => {
        const success = steps.every((step) => step.success);
        return {
            module: 'user',
            overallStatus: success ? 'PASSED' : 'FAILED',
            success,
            entityId,
            steps
        };
    }

    private randomDigits = (length: number): string => {
        let value = '';
        for (let i = 0; i < length; i++) {
            value += Math.floor(Math.random() * 10).toString();
        }
        return value;
    }

    private generateCreatePayload = () => {
        const seed = `${Date.now()}-${Math.floor(Math.random() * 100000)}`;
        return {
            email: `flow-user-${seed}@example.com`,
            countryCode: '+1',
            mobile: `9${this.randomDigits(9)}`,
            password: `FlowPass${Math.floor(Math.random() * 9000) + 1000}`,
            name: `flow-user-${seed}`,
            roles: ['user']
        };
    }

    // signup with email/username
    signup = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { email, firstName, lastName, rankId, password, referralCode } = request.payload as SignUpEmailObject;
            const name = `${firstName || ''} ${lastName || ''}`.trim();
            let signUpToken = await userService.generateSignUpToken({ email: email, name, firstName, lastName, rankId, password: password, accountKey: request.headers.account as any, referralCode: referralCode });
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: signUpToken }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Create with email/username
    create = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { email, name, password, mobile, countryCode, roles, socialMediaLinks, attachmentId, aboutMe, nationality } = request.payload as any;
            const signupData: SignUpEmailObject = { email: email, name: name, password: password, accountKey: request.headers.account as any, referralCode: null, roles: roles, mobile, countryCode, socialMediaLinks, profileImageId: attachmentId, aboutMe, nationality };
            const createNewAccountInput: UserCreateNewAccountServiceInput = { signupData, createUser: true };
            let userData = await userService.createNewAccount(createNewAccountInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: JSON.parse(JSON.stringify(userData)) }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Create with email/username
    setPassword = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { password } = request.payload as { password: string };
            const userId = request.params.id;
            const setPasswordInput: UserSetPasswordServiceInput = { userId: +userId, password };
            let userData = await userService.setPassword(setPasswordInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: userData }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    update = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const id = request.params.id;
            let { email, name, password, mobile, countryCode, roles, socialMediaLinks, attachmentId, aboutMe, nationality } = request.payload as any;
            const signupData: SignUpEmailObject = { email: email, name: name, password: password, accountKey: request.headers.account as any, referralCode: null, roles: roles, mobile, countryCode, socialMediaLinks, profileImageId: attachmentId, aboutMe, nationality };
            const updateUserDetailsInput: UserUpdateUserDetailsServiceInput = { id: +id, signupData };
            let userData = await userService.updateUserDetails(updateUserDetailsInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: JSON.parse(JSON.stringify(userData)) }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // verifyEmail token with code sent to email
    verifyEmail = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { signUpToken, changeEmailToken, forgotPasswordToken, code, type } = request.payload as verifyEmailRequest;
            let tokenToVerify = signUpToken ? signUpToken : (changeEmailToken ? changeEmailToken : forgotPasswordToken);
            if (!tokenToVerify) {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
            let verifyToken: SignUpEmailObject = await userService.verifyToken(tokenToVerify, code, type!);
            if (verifyToken) {
                // create user with default user
                if (type === 'signup') {
                    const createAccountInput: UserCreateServiceInput = { signupData: verifyToken };
                    let newUser = await userService.createAccount(createAccountInput);
                    await SessionService.setSession(request, newUser.accountId, newUser.id, newUser.token);
                    return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: newUser }).code(200);
                }
                else if (type === 'changeEmail') {
                    if (verifyToken.email) {
                        const userData = await userService.validateUpdateEmailAndLogin(verifyToken.userId!, 'email', verifyToken.email);
                        return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: userData }).code(200);
                    }
                }
            } else {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // verifyMobile token with code sent to mobile
    verifyMobile = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { mobileLoginToken, changeMobileToken, code } = request.payload as verifyMobileRequest;

            let tokenToVerify;
            let tokenType;

            if (mobileLoginToken) {
                tokenToVerify = mobileLoginToken;
                tokenType = "mobileLogin";
            } else if (changeMobileToken) {
                tokenToVerify = changeMobileToken;
                tokenType = "changeMobile";
            }

            if (!tokenToVerify) {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
            //let verifyToken: SignUpEmailObject = await userService.verifyToken(tokenToVerify, code, 'mobileLogin');
            let verifyToken: SignUpEmailObject = await userService.verifyOTP(tokenToVerify, code, tokenType!);
            if (verifyToken) {

                switch (tokenType) {
                    case "mobileLogin":
                        {
                            if (verifyToken.userId) {
                                if (verifyToken.mobile) {
                                    const userData = await userService.mobileLogin(verifyToken?.userId!);
                                    await SessionService.setSession(request, userData.accountId, userData.id, userData.token);
                                    return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: userData }).code(200);
                                }
                            } else {
                                const createAccountInput: UserCreateServiceInput = { signupData: verifyToken };
                                let newUser = await userService.createAccount(createAccountInput);
                                await SessionService.setSession(request, newUser.accountId, newUser.id, newUser.token);
                                return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: newUser }).code(200);
                            }
                        }
                        break;

                    case "changeMobile":
                        {
                            if (verifyToken.userId) {
                                if (verifyToken.mobile) {
                                    const userData = await userService.validateUpdateMobileAndLogin(verifyToken?.userId!, verifyToken.countryCode!, verifyToken.mobile);
                                    await SessionService.setSession(request, userData.accountId, userData.id, userData.token);
                                    return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: userData }).code(200);
                                }
                            }
                        }
                        break;
                    default:
                        break;
                }


            } else {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // verifyEmail token with code sent to email
    resendCode = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { signUpToken, changeEmailToken, changeMobileToken, forgotPasswordToken, mobileLoginToken, onCall } = request.payload as resendToken;
            let resendToken: resendToken;
            if (signUpToken) {
                resendToken = await userService.resendCode(signUpToken, 'signup');
            } else if (changeEmailToken) {
                resendToken = await userService.resendCode(changeEmailToken, 'changeEmail');
            } else if (mobileLoginToken) {
                resendToken = await userService.resendCode(mobileLoginToken, 'mobileLogin', onCall);
            } else if (changeMobileToken) {
                resendToken = await userService.resendCode(changeMobileToken, 'changeMobile', onCall);
            }
            else if (forgotPasswordToken) {
                resendToken = await userService.resendCode(forgotPasswordToken, 'forgotPassword');
            }
            else {
                throw new AppError(400, 'INVALID_TOKEN_TYPE', {});
            }
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: resendToken }).code(200);

        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // login with password
    login = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { email, username, countryCode, mobile, password } = request.payload as loginRequest;
            let authenticationData;
            if (email) { // login with email and password
                authenticationData = await userService.loginWithEmailPassword(email, password);
            } else if (username) { // login with username and password
                authenticationData = await userService.loginWithUsernamePassword(username, password)
            } else if (countryCode && mobile) { // login with mobile password
                authenticationData = await userService.loginWithMobilePassword(countryCode, mobile, password)
            } else {
                throw new AppError(404, 'NVALID_LOGIN_METHOD', {});
            }
            userService.logUserActivity('login', request.app.clientIp ? request.app.clientIp : '-', { name: authenticationData.userProfile.name, ip: request.app.clientIp ? request.app.clientIp : '-' }, request.app.deviceInfo, true, authenticationData.id).catch(err => { });
            await SessionService.setSession(request, authenticationData.accountId, authenticationData.id, authenticationData.token);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: authenticationData }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // login with password
    logout = async (request: Request, h: ResponseToolkit) => {
        try {
            let { removeAll } = request.payload as LogoutRequest;
            let userId = request.auth?.isAuthenticated ? (request.auth.credentials as unknown as AuthData).userData.userId : null
            let accountId = request.auth?.isAuthenticated ? (request.auth.credentials as unknown as AuthData).userData.accountId : null
            let token = request.headers.authorization
            if (userId) {
                await SessionService.remove(request, accountId, userId, token, removeAll);
            }
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // forgot password
    forgotPassword = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { email } = request.payload as ForgotPasswordObject;
            const language: string = (request.headers?.language || process.env.DEFAULT_LANGUAGE_CODE || 'en') as string;
            let resetPassordToken = await userService.getResetPasswordToken(email, request.headers.account);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: resetPassordToken }).code(200)
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // reset password
    resetPassword = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { resetPasswordToken, verificationCode, password } = request.payload as ResetPasswordObject;
            let verifyToken: resetPasswordRequest = await userService.verifyToken(resetPasswordToken, verificationCode, 'forgotPassword');
            if (verifyToken) {
                const user = await userService.resetPassword(verifyToken.email, password);
                return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: user }).code(200)

            } else {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }

        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Change password
    changePassword = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { password } = request.payload as ChangePasswordRequest;
            const userId = (request.auth.credentials as unknown as AuthData).userData.userId;
            const email = (request.auth.credentials as unknown as AuthData).userData.email;
            const name = (request.auth.credentials as unknown as AuthData).userData.name;
            await userService.changePassword(userId, name, email, password)
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200)
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Update password
    updatePassword = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { currentPassword, newPassword, confirmNewPassword } = request.payload as any;
            if (newPassword !== confirmNewPassword) {
                throw new AppError(400, 'CONFIRM_PASSWORD_MISMATCH', { confirmNewPassword: 'CONFIRM_PASSWORD_MISMATCH' });
            }
            const userId = (request.auth.credentials as unknown as AuthData).userData.userId;
            const email = (request.auth.credentials as unknown as AuthData).userData.email;
            const name = (request.auth.credentials as unknown as AuthData).userData.name;
            await userService.updatePassword(userId, name, email, currentPassword, newPassword);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // list Users with filter
    list = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let listRequest = request.query as UserPaginatioinListRequestObject;
            let { categoryTypeCode } = request.params as CategoryTypeCodeIdentifierObject;
            let users: UserPaginatedList = await userService.getUsers(listRequest);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // public list Users with filter
    publicList = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let listRequest = request.query as UserPaginatioinListRequestObject;
            let users: UserPaginatedList = await userService.getUsers(listRequest);

            const loggedInUserId = (request.auth.credentials as any)?.userData?.userId;
            let followedByMeIds: number[] = [];

            if (loggedInUserId && users.data && users.data.length > 0) {
                const returnedUserIds = users.data.map((u: any) => u.id).filter(Boolean);
                const followed = await Models.UserFollower.findAll({
                    where: { followerId: loggedInUserId, followingId: returnedUserIds },
                    attributes: ['followingId']
                });
                followedByMeIds = followed.map((f: any) => f.followingId);
            }

            // Map the data to restrict returned profile info
            const restrictedData = users.data.map((user: any) => {
                return {
                    id: user.id,
                    email: user.email,
                    isFollowing: followedByMeIds.includes(user.id),
                    followersCount: user.userProfile?.followersCount || 0,
                    followingCount: user.userProfile?.followingCount || 0,
                    userProfile: {
                        name: user.userProfile?.name,
                        profileImage: user.userProfile?.profileImage,
                        rank: user.userProfile?.rank,
                        followersCount: user.userProfile?.followersCount || 0,
                        followingCount: user.userProfile?.followingCount || 0
                    }
                };
            });
            users.data = restrictedData as any;

            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // list Users with filter
    referralList = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { page, perPage } = request.query;
            let users = await userService.referralList(page, perPage);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    referralListOfUser = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { page, perPage } = request.query;
            const userId = request.params.id;
            let users = await userService.referralList(page, perPage, userId);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // list all Users with filter
    listAll = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let listRequest = request.query as UserPaginatioinListRequestObject;
            let users: UserPaginatedList = await userService.getAllUsers(listRequest);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // token using referesh token
    tokenWithRefreshToken = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { refreshToken } = request.payload as TokenFromRefreshTokenRequest;
            let tokenPair: RefTokenResponse = await userService.tokenWithRefreshToken(refreshToken, 'refreshToken');
            if (tokenPair) {
                let userId = tokenPair.userId;
                let accountId = tokenPair.accountId;
                if (userId) {
                    await SessionService.setSession(request, accountId, userId, tokenPair.token);
                    return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: { token: tokenPair.token, refreshToken: tokenPair.refreshToken } }).code(200);
                } else {
                    throw new AppError(400, 'INVALID_EXPIRED_TOKEN', { refreshToken: 'INVALID_EXPIRED_TOKEN' });
                }
            } else {
                throw new AppError(400, 'INVALID_EXPIRED_TOKEN', { refreshToken: 'INVALID_EXPIRED_TOKEN' });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // export Users
    export = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const passThrough = new PassThrough();
            let listRequest = request.query as UserPaginatioinListRequestObject;
            listRequest.perPage = +process.env.LIST_ALL_LIMIT!
            try {
                let page = 1
                const csvStream = csv.format({ headers: true });
                csvStream.pipe(passThrough);
                let processing = true;
                while (processing) {
                    let users: UserPaginatedList = await userService.getUsers(listRequest);
                    if (!users.data.length) {
                        processing = false;
                        break;
                    }
                    for (let row of users.data) {
                        row = Common.flattenObject(JSON.parse(JSON.stringify(row)));
                        csvStream.write(row);
                    }
                    page++;
                }
                csvStream.end();
            } catch (err) {
                console.log(err);
                passThrough.destroy(err as Error);
            }
            console.log("returning file", passThrough)
            return h.response(passThrough)
                .header('Content-Type', 'text/csv')
                .header('Content-Disposition', 'attachment; filename="user_export.csv"');
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Get user profile information
    profile = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const authUserId = request.auth?.isAuthenticated
                ? (request.auth.credentials as unknown as AuthData).userData.userId
                : null;
            const queryUserId = request.query?.id;
            const userId = queryUserId ?? authUserId;
            if (!userId) {
                throw new AppError(401, 'UNAUTHORIZED_ACCESS');
            }
            let fullObject = (authUserId == userId) ? true : false;
            let user = await userService.getUserById(userId, fullObject, true);

            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: JSON.parse(JSON.stringify(user)) }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Get user profile information
    userProfile = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const userId = request.params.id;
            let fullObject = true;
            let user = await userService.getUserProfile(userId, true);

            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: JSON.parse(JSON.stringify(user)) }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    // Handle social login for users
    socialLogin = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { provider, accessToken, email, name, phone } = request.payload as socialLoginRequest;
            // Pass email or phone as identifier    
            let phoneNumber = null;
            let countryCode = null;
            if (phone) {
                phoneNumber = parsePhoneNumberFromString(phone);
                if (phoneNumber) {
                    countryCode = phoneNumber.countryCallingCode;
                    phoneNumber = phoneNumber.nationalNumber;
                }
            }
            const identifier = email ?? phoneNumber ?? null;

            const verifyAccessToken = await userService.verifySocialLogin(provider, accessToken, identifier);
            if (!verifyAccessToken) {
                throw new AppError(400, 'INVALID_TOKEN');
            }
            const profileName = name ?? email?.split("@")[0] ?? verifyAccessToken.sub?.slice(0, 10) ?? null;

            const userData = await userService.socialLogin(provider, profileName, countryCode, verifyAccessToken);
            await SessionService.setSession(request, userData.accountId, userData.id, userData.token);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: userData }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    // Register user device
    registerUserDevice = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { deviceType, device } = request.payload as UserDeviceRequest;
            if (!deviceType || !device) {
                throw new AppError(400, 'REQUIRED_DEVICE_INFORMATION');
            }
            const registerUserDeviceInput: UserRegisterUserDeviceServiceInput = { deviceType, device };
            const reqisterDevice = await userService.registerUserDevice(registerUserDeviceInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200)
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    changeEmail = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { email } = request.payload as changeEmailRequest;
            let changeEmailToken = await userService.generateChangeEmailToken({ email: email, accountKey: request.headers.account });
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: changeEmailToken }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    changeMobile = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { countryCode, mobile } = request.payload as mobileLoginRequest;
            let changeMobileToken = await userService.generateChangeMobileToken({ countryCode, mobile, accountKey: request.headers.account });
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: changeMobileToken }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    mobileLogin = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { countryCode, mobile } = request.payload as mobileLoginRequest;
            let mobileLoginToken = await userService.generateMobileLoginToken({ countryCode: countryCode, mobile: mobile, name: mobile, accountKey: request.headers.account });
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: mobileLoginToken }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    deleteAccount = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { countryCode, mobile } = request.payload as mobileLoginRequest;
            let mobileLoginToken = await userService.deleteAccount(countryCode, mobile);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: mobileLoginToken }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    deleteAuthorizUserAccount = async (request: Request, h: ResponseToolkit) => {
        try {
            let userId = request.auth?.isAuthenticated ? (request.auth.credentials as unknown as AuthData).userData.userId : null
            if (!userId) {
                throw new AppError(400, 'REQUIRED_USER_ID');
            }
            let userService = await this.getUserServiceObject(request);
            const response = await userService.deleteAuthorizeUserAccount(userId!);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: response }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    verifyDeleteAccount = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            let { deleteAccountToken, code } = request.payload as verifyMobileRequest;

            const tokenToVerify = deleteAccountToken;
            const tokenType = "deleteAccount";


            if (!tokenToVerify) {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
            //let verifyToken: SignUpEmailObject = await userService.verifyToken(tokenToVerify, code, 'mobileLogin');
            let verifyToken: SignUpEmailObject = await userService.verifyOTP(tokenToVerify, code, tokenType!);
            if (verifyToken && verifyToken.userId) {

                if (verifyToken.mobile) {
                    const deleteUserInput: UserDeleteServiceInput = { userId: verifyToken?.userId!, countryCode: verifyToken.countryCode!, mobile: verifyToken.mobile };
                    const userData = await userService.deleteUser(deleteUserInput);
                    //await SessionService.setSession(request, userData.accountId, userData.id, userData.token);
                    return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: {} }).code(200);
                }


            } else {
                throw new AppError(400, 'INVALID_EXPIRED_CODE', { code: 'INVALID_EXPIRED_CODE' });
            }
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    }

    updateProfile = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const {
                profileImageId,
                name,
                dob,
                rankId,
                presentRankSince,
                presentCompany,
                presentCompanySince,
                aboutMe,
                areaOfExpertise,
                socialMediaLinks,
                nationality
            } = request.payload as UpdateUserProfileRequest;

            const fields: Record<string, any> = {};

            if (name !== undefined) fields.name = name;
            if (dob !== undefined) fields.dob = dob;
            if (rankId !== undefined) fields.rankId = rankId;
            if (presentRankSince !== undefined) fields.presentRankSince = presentRankSince;
            if (presentCompany !== undefined) fields.presentCompany = presentCompany;
            if (presentCompanySince !== undefined) fields.presentCompanySince = presentCompanySince;
            if (aboutMe !== undefined) fields.aboutMe = aboutMe;
            if (areaOfExpertise !== undefined) fields.areaOfExpertise = areaOfExpertise;
            if (socialMediaLinks !== undefined) fields.socialMediaLinks = socialMediaLinks;
            if (nationality !== undefined) fields.nationality = nationality;

            if (profileImageId !== undefined) {
                if (profileImageId !== null) {
                    // validate image first
                    let attachmentService = await this.getAttachmentServiceObject(request);
                    const emailTemplateAttachments = [profileImageId];
                    const verifyAttachments = await attachmentService.verifyFile(emailTemplateAttachments);
                    if (verifyAttachments.length !== emailTemplateAttachments.length) {
                        throw new AppError(400, 'INVALID_DUPLICATE_ATTACHMENT_FOUND', { emailTemplateAttachments: 'INVALID_DUPLICATE_ATTACHMENT_FOUND' });
                    }
                }
                fields.profileImageId = profileImageId;
            }

            await userService.updateUserProfileFields(fields);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    updateReferralCode = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { referralCode } = request.payload as { referralCode: string };
            const userId = request.params.id;
            const updateReferralCodeInput: UserUpdateReferralCodeServiceInput = { userId: +userId, referralCode };
            await userService.updateReferralCode(updateReferralCodeInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    updateStatus = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { id } = request.params;
            const { status } = request.payload as UpdateUserStatusRequest;
            if (status != undefined) {
                await userService.updateUserStatus(status, id);
                if (status == 0) {
                    // reset session
                    let userData = (request.auth.credentials as unknown as AuthData).userData
                    await SessionService.remove(request, userData.accountId, id, null, true);
                }
            }
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    applyReferral = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { referralCode } = request.payload as { referralCode: string };
            const applyReferralInput: UserApplyReferralServiceInput = { referralCode };
            await userService.applyReferral(applyReferralInput);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    securityDeclaration = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            await userService.securityDeclaration();
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY" }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    toggleFollow = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const targetUserId = request.params.id;
            const authUserId = (request.auth.credentials as unknown as AuthData).userData.userId;
            const followStatus = await userService.toggleFollow(+authUserId, +targetUserId);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: followStatus }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    followersList = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const targetUserId = request.params.id;
            let listRequest = request.query as UserPaginatioinListRequestObject;
            let users = await userService.followersList(+targetUserId, listRequest);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    followingList = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            let userService = await this.getUserServiceObject(request);
            const targetUserId = request.params.id;
            let listRequest = request.query as UserPaginatioinListRequestObject;
            let users = await userService.followingList(+targetUserId, listRequest);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: users }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    runFlow = async (request: Request, h: ResponseToolkit): Promise<ResponseObject> => {
        try {
            const { detailed } = request.query as UserFlowQueryObject;
            const includeDetailed = detailed === true || detailed === 'true';
            const headers = this.getForwardHeaders(request);
            const steps: UserFlowStepObject[] = [];
            let userId: number | null = null;

            const runStep = async (
                step: string,
                method: string,
                endpoint: string,
                payload: unknown = null,
                expectFailure: boolean = false
            ) => {
                const response = await request.server.inject({
                    method: method as any,
                    url: endpoint,
                    headers: { ...headers, ...(payload ? { 'content-type': 'application/json' } : {}) },
                    payload: payload ?? undefined
                });
                const result = response.result as any;
                const is2xx = response.statusCode >= 200 && response.statusCode < 300;
                const success = expectFailure ? !is2xx : is2xx;
                const stepObject: UserFlowStepObject = {
                    step,
                    method,
                    endpoint,
                    statusCode: response.statusCode,
                    success,
                    responseMessage: success ? (result?.message || null) : null,
                    errorMessage: success ? null : (result?.message || response.statusMessage || 'REQUEST_FAILED')
                };
                if (includeDetailed) {
                    stepObject.payload = payload ?? null;
                    stepObject.response = result ?? response.payload;
                }
                steps.push(stepObject);
                return { success, result };
            };

            let createPayload: any = null;
            let createStep: { success: boolean; result: any } | null = null;
            for (let attempt = 1; attempt <= 10; attempt++) {
                createPayload = this.generateCreatePayload();
                createStep = await runStep(`create-${attempt}`, 'POST', '/user/create', createPayload);
                if (createStep.success) break;
            }
            if (!createStep || !createStep.success) {
                return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: this.buildFlowResponse(steps, userId) }).code(200);
            }

            userId = createStep.result?.responseData?.id ?? null;
            if (!userId) {
                steps.push({
                    step: 'extractId',
                    method: 'INTERNAL',
                    endpoint: '/user/create',
                    statusCode: 500,
                    success: false,
                    responseMessage: null,
                    errorMessage: 'USER_ID_NOT_FOUND_IN_CREATE_RESPONSE'
                });
                return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: this.buildFlowResponse(steps, userId) }).code(200);
            }

            const updatePayload = {
                ...createPayload,
                name: `${createPayload.name}-updated`,
                email: `updated-${createPayload.email}`
            };
            const passwordPayload = {
                password: `FlowPass${Math.floor(Math.random() * 9000) + 1000}`
            };
            const updateReferralPayload = {
                referralCode: `FLOW-${Date.now()}-${Math.floor(Math.random() * 1000)}`
            };
            const inactiveStatusPayload = { status: 0 };
            const activeStatusPayload = { status: 1 };

            const stepsOrder: Array<{ step: string; method: string; endpoint: string; payload?: unknown; expectFailure?: boolean; }> = [
                { step: 'userProfileAfterCreate', method: 'GET', endpoint: `/user/${userId}/profile` },
                { step: 'updateUser', method: 'PATCH', endpoint: `/user/${userId}/update`, payload: updatePayload },
                { step: 'setPassword', method: 'PATCH', endpoint: `/user/${userId}/set-password`, payload: passwordPayload },
                { step: 'updateReferralCode', method: 'PATCH', endpoint: `/user/${userId}/update-referral-code`, payload: updateReferralPayload },
                { step: 'deactivateUser', method: 'PATCH', endpoint: `/user/status/${userId}`, payload: inactiveStatusPayload },
                {
                    step: 'loginWhileInactive',
                    method: 'POST',
                    endpoint: '/user/login/mobile-password',
                    payload: {
                        countryCode: createPayload.countryCode,
                        mobile: createPayload.mobile,
                        password: passwordPayload.password
                    },
                    expectFailure: true
                },
                { step: 'activateUser', method: 'PATCH', endpoint: `/user/status/${userId}`, payload: activeStatusPayload },
                {
                    step: 'loginAfterActivate',
                    method: 'POST',
                    endpoint: '/user/login/mobile-password',
                    payload: {
                        countryCode: createPayload.countryCode,
                        mobile: createPayload.mobile,
                        password: passwordPayload.password
                    }
                }
            ];

            for (const flowStep of stepsOrder) {
                const response = await runStep(
                    flowStep.step,
                    flowStep.method,
                    flowStep.endpoint,
                    flowStep.payload,
                    flowStep.expectFailure ?? false
                );
                if (!response.success) break;
            }

            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: this.buildFlowResponse(steps, userId) }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_SERVICE', err);
        }
    };

    deleteUser = async (request: Request, h: ResponseToolkit) => {
        try {
            let userService = await this.getUserServiceObject(request);
            const { id } = request.params as any;
            await userService.deleteUserByAdmin(+id);
            return h.response({ message: "REQUEST_PROCESSED_SUCCESSFULLY", responseData: {} }).code(200);
        } catch (err) {
            if (err instanceof AppError) { throw err; }
            throw new AppError(500, 'SOMETHING_WENT_WRONG_IN_HANDLER', err);
        }
    };
}
