import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import * as bcrypt from "bcrypt"
import * as jwt from "jsonwebtoken"
import moment from "moment"
import { mailSubject } from "../../common/emails/email-subjects"
import { changePasswordIntimation } from "../../common/emails/templates/change-password-intimation"
import { forgotPasswordToken } from "../../common/emails/templates/forgot-password"
import { passwordSetupEmail } from "../../common/emails/templates/password-setup"
import { emailChangeRequestTemplate } from "../../common/emails/templates/email-change-request"
import { demoRequestEmail } from "../../common/emails/templates/demo-request"
import { featuresShowcaseEmail } from "../../common/emails/templates/features-showcase"
import * as XLSX from "xlsx"
import { fileStoreLocation } from "../../common/file-upload/file-store-location"
import {
  failureResponse,
  successResponse,
} from "../../common/response/response"
import { code } from "../../common/response/response.code"
import { appSetting } from "../../config/app.config"
import { messageKey } from "../../constants/message-keys"
import {
  encryptPassword,
  errorMessage,
  generateResponseObject,
  isEmpty,
  sendEmailNotification,
  successMessage,
  validationMessage,
} from "../../utils/helpers"
import { generateJwtToken, JwtPayload, verifyJwtToken } from "../../utils/jwt"
import { profileResponse } from "./auth.remove.columns"
import { ChangePasswordDto } from "./dto/change-password.dto"
import { CreateUserDto } from "./dto/create-user.dto"
import { EditProfileDto } from "./dto/edit-profile.dto"
import { LoginDto } from "./dto/login.dto"
import { ResetPasswordDto } from "./dto/reset-password.dto"
import { SetPasswordDto } from "./dto/set-password.dto"
import { RequestDemoDto } from "./dto/request-demo.dto"
import { Auth } from "./entities/auth.entity"
import { UserLogin } from "./entities/user-login.entity"
import { AuthRepository } from "./repositories/auth.repository"
import { UserLoginRepository } from "./repositories/user-login.repository"
import { UserBlacklistedTokenRepository } from "./repositories/user-blacklisted-token.repository"
import { PasswordSetupTokenRepository } from "./repositories/password-setup-token.repository"
import { EmployeeRepository } from "../employees/repositories/employee.repository"
import { RolePermissionsService } from "../role-permissions/role-permissions.service"
import { RoleService } from "../role/role.service"
import { CompanySubscriptionRepository } from "../company/repositories/company-subscription.repository"
import { SubscriptionStatus } from "../company/entities/company-subscription.entity"
import { EV } from "src/utils/env.values"
import { CompanyProfileRepository } from "../company/repositories/company-profile.repository"

// Interfaces for bulk email functionality
interface BulkEmailError {
  row_number: number
  name: string
  email: string
  error_message: string
}

interface BulkEmailResponse {
  total_records: number
  success_count: number
  failure_count: number
  errors: BulkEmailError[]
  error_file?: Buffer
}

@Injectable()
export class AuthService {
  constructor(
    private readonly authRepository: AuthRepository,
    private readonly userLoginRepository: UserLoginRepository,
    private readonly userBlacklistedTokenRepository: UserBlacklistedTokenRepository,
    private readonly passwordSetupTokenRepository: PasswordSetupTokenRepository,
    private readonly employeeRepository: EmployeeRepository,
    private readonly rolePermissionsService: RolePermissionsService,
    private readonly roleService: RoleService,
    private readonly companySubscriptionRepository: CompanySubscriptionRepository,
    private readonly configService: ConfigService,
    private readonly companyProfileRepository: CompanyProfileRepository,
  ) {}

  async login(loginDto: LoginDto) {
    try {
      const user: any = await this.authRepository.getByParams({
        where: {
          email: loginDto.email.toLowerCase(),
        },
        findOne: true,
        relations: ["role", "company"],
      })

      if (!isEmpty(user)) {
        // Check if email is verified
        if (!user.is_email_verified) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.email_not_verified),
          )
        }

        // Check if account is active
        if (user.status === 0) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.user_account_inactive),
          )
        }

        // Check if password is set (not null/empty)
        if (!user.password || user.password === "") {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.password_not_set),
          )
        }

        if (await this.comparePassword(loginDto.password, user.password)) {
          // Check if user is linked to an employee, if not try to link
          let employeeId = user.employee_id
          let employeeRoleId = user.role_id // Default to user's role_id

          if (!employeeId && user.company_id) {
            const employee = await this.employeeRepository.getByParams({
              where: {
                email: user.email.toLowerCase(),
                company_id: user.company_id,
              },
              findOne: true,
              relations: ["role"], // Join with role table
            })

            if (!isEmpty(employee)) {
              // Link user to employee
              await this.linkUserToEmployee(user.id, (employee as any).id)
              employeeId = (employee as any).id

              // Use employee's role_id if available
              if ((employee as any).role_id) {
                employeeRoleId = (employee as any).role_id
              }
            }
          } else if (employeeId) {
            // If employee is already linked, fetch employee with role to get role_id
            const employee = await this.employeeRepository.getByParams({
              where: {
                id: employeeId,
              },
              findOne: true,
              relations: ["role"], // Join with role table
            })

            if (!isEmpty(employee) && (employee as any).role_id) {
              employeeRoleId = (employee as any).role_id
            }
          }

          // Validate department status for non-master admin users
          if (employeeId) {
            try {
              await this.validateEmployeeDepartmentStatus(
                employeeId,
                employeeRoleId,
              )
            } catch (error) {
              const errorData = JSON.parse(error.message)
              return errorData
            }
          }

          // Validate company subscription for non-master admin users
          if (user.company_id) {
            try {
              await this.validateCompanySubscriptionStatus(
                employeeRoleId,
                user.company_id,
              )
            } catch (error) {
              const errorData = JSON.parse(error.message)
              return errorData
            }
          }

          // Prepare JWT payload
          const jwtPayload: JwtPayload = {
            user_id: user.id,
            role_id: employeeRoleId,
            company_id: user.company_id || null,
            employee_id: employeeId || null,
          }

          // Generate access token using JWT
          const accessToken = generateJwtToken(jwtPayload)

          // Generate refresh token using JWT
          const refreshToken = this.generateRefreshToken(jwtPayload)
          const refreshTokenExpiry: any = moment
            .utc()
            .add(appSetting.refresh_token_expiry_in_days, "days")
            .format()

          const userExistingLogin: any =
            await this.userLoginRepository.getByParams({
              where: {
                user_id: user.id,
              },
              findOne: true,
            })

          if (!isEmpty(userExistingLogin)) {
            // Update existing login record with new refresh token
            userExistingLogin.refresh_token = refreshToken
            userExistingLogin.refresh_token_expire_at = refreshTokenExpiry
            await this.userLoginRepository.save(userExistingLogin)
          } else {
            // Create new login record
            const userLogin = new UserLogin()
            userLogin.user_id = user.id
            userLogin.refresh_token = refreshToken
            userLogin.refresh_token_expire_at = refreshTokenExpiry
            await this.userLoginRepository.save(userLogin)
          }

          user.access_token = accessToken

          // Get user permissions
          let permissions: any = {}

          if (employeeRoleId) {
            permissions =
              await this.rolePermissionsService.getRolePermissions(accessToken)
          }

          // Detect Super Admin based on role name and update slug if needed
          const isSuperAdmin = user.role && user.slug === "super_admin"

          // Remove "manage_time_tracking" from ALL permission groups
          if (permissions.data && isSuperAdmin) {
            Object.keys(permissions.data).forEach((groupKey) => {
              if (Array.isArray(permissions.data[groupKey])) {
                permissions.data[groupKey] = permissions.data[groupKey].filter(
                  (permission: any) =>
                    permission.permission_key !== "manage_time_tracking",
                )
              }
            })
          }

          // Inject employee-specific permission inside "leaves"

          if (employeeId) {
            if (
              permissions.data &&
              permissions.data.leaves &&
              Array.isArray(permissions.data.leaves)
            ) {
              permissions.data.leaves.push({
                id: employeeId,
                permission_key: "view_my_leaves",
                label: "View My Leaves",
                description: "Permission to view own leaves",
              })
            }

            if (!permissions.data) {
              permissions.data = {}
            }

            // Ensure payroll array exists
            if (!Array.isArray(permissions.data.payroll)) {
              permissions.data.payroll = []
            }

            // Add permission only if not already added
            const alreadyAdded = permissions.data.payroll.some(
              (p: any) => p.permission_key === "view_my_salary_slip",
            )

            if (!alreadyAdded) {
              permissions.data.payroll.push({
                id: employeeId,
                permission_key: "view_my_salary_slip",
                label: "View My Salary Slip",
                description: "Permission to view own salary slip",
              })
            }
          }

          if (user.company_id) {
            const companyProfile: any =
              await this.companyProfileRepository.getByParams({
                where: {
                  company_id: user.company_id,
                },
                findOne: true,
              })

            if (companyProfile) {
              user.country_code = companyProfile.country_code || 91
              user.currency = companyProfile.currency || "₹"
            }
          }

          const userObj: any = {
            user: {
              id: user.id,
              employee_id: user.employee_id,
              first_name: user.first_name,
              last_name: user.last_name,
              email: user.email,
              phone: user.phone,
              profile_image: user.profile_image,
              company_id: user.company_id,
              role_id: employeeRoleId,
              company: user.company
                ? { id: user.company.id, name: user.company.name }
                : null,
              role: user.role
                ? { id: user.role.id, name: user.role.name }
                : null,
              country_code: user.country_code || 91,
              currency: user.currency || "₹",
              permissions: permissions,
              slug: user.slug,
            },
            tokens: {
              access_token: accessToken,
              refresh_token: refreshToken,
            },
          }

          return successResponse(
            code.SUCCESS,
            successMessage(messageKey.login_success),
            userObj,
          )
        }
      }

      const message = errorMessage(messageKey.invalid_credentials)
      return failureResponse(code.INVALID_CREDENTIALS, message)
    } catch (error) {
      console.error("Error in login:", error)
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.exception),
      )
    }
  }

  /**
   * Generates a refresh token using JWT
   *
   * @param {JwtPayload} payload - The JWT payload containing user information
   * @return {string} The generated refresh token
   */
  generateRefreshToken(payload: JwtPayload): string {
    return generateJwtToken(payload, true)
  }

  /**
   * Regenerates a new refresh token for an existing user session
   *
   * @param {string} oldRefreshToken - The old refresh token to be replaced
   * @return {Promise<any>} Success or failure response with new tokens
   */
  async regenerateRefreshToken(oldRefreshToken: string) {
    try {
      // Verify the old refresh token (allow expired tokens for refresh)
      const decoded = await this.verifyToken(oldRefreshToken)

      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Find the user login record
      const userLogin: any = await this.userLoginRepository.getByParams({
        where: {
          refresh_token: oldRefreshToken,
          user_id: decoded.user_id,
        },
        findOne: true,
      })

      if (isEmpty(userLogin)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Check if refresh token is expired
      const tokenExpiry = moment(userLogin.refresh_token_expire_at)
        .utc(true)
        .valueOf()
      const currentTime = moment().utc().valueOf()

      if (currentTime >= tokenExpiry) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.token_expire),
        )
      }

      // Generate new tokens
      const jwtPayload: JwtPayload = {
        user_id: decoded.user_id,
        role_id: decoded.role_id,
        company_id: decoded.company_id || null,
        employee_id: decoded.employee_id || null,
      }

      const newAccessToken = generateJwtToken(jwtPayload)
      const newRefreshToken = this.generateRefreshToken(jwtPayload)
      const newRefreshTokenExpiry: any = moment
        .utc()
        .add(appSetting.refresh_token_expiry_in_days, "days")
        .format()

      // Update the refresh token in database
      userLogin.refresh_token = newRefreshToken
      userLogin.refresh_token_expire_at = newRefreshTokenExpiry
      await this.userLoginRepository.save(userLogin)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.token_refresh_success),
        {
          access_token: newAccessToken,
          refresh_token: newRefreshToken,
        } as any,
      )
    } catch (error) {
      console.error("Error in regenerateRefreshToken:", error)
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.exception),
      )
    }
  }

  /**
   * Verifies a JWT token and returns the decoded payload
   *
   * @param {string} token - The JWT token to verify
   * @param {boolean} allowExpired - Whether to allow expired tokens (for refresh token verification)
   * @return {Promise<JwtPayload | null>} The decoded payload or null if invalid
   */

  private async verifyToken(token: string): Promise<JwtPayload | null> {
    try {
      const { verifyJwtToken } = await import("../../utils/jwt")
      return verifyJwtToken(token)
    } catch (error) {
      return null
    }
  }

  async forgotPassword(email: string) {
    try {
      const user: any = await this.authRepository.getByParams({
        where: {
          email: email.toLowerCase(),
        },
        findOne: true,
      })

      if (isEmpty(user)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.user_not_found),
        )
      }

      const token = await this.generatePasswordSetupToken(user.id)
      const setupLink = `${EV["FRONT_URL"]}set-password/${token}`

      await sendEmailNotification(
        email.toLowerCase(),
        forgotPasswordToken(user.first_name || "User", setupLink),
        mailSubject.forgotPasswordSubject,
      )

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.email_sent),
      )
    } catch (error) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.email_send_failed),
      )
    }
  }

  async resetPassword(resetPasswordDto: ResetPasswordDto) {
    const user: any = await this.authRepository.getByParams({
      where: {
        email: resetPasswordDto.email.toLowerCase(),
      },
      findOne: true,
    })

    if (isEmpty(user)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_otp),
      )
    }

    if (user.password_reset_otp !== resetPasswordDto.otp) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_otp),
      )
    }

    let authUser = new Auth()
    authUser = { ...user }
    const encryptedPassword = await encryptPassword(resetPasswordDto.password)
    authUser.password = encryptedPassword

    // Auto-verify email and activate account after successful password reset
    authUser.is_email_verified = true
    authUser.status = 1
    authUser.email_verification_token = null // Clear any existing verification token

    await this.authRepository.save(authUser)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.password_update),
    )
  }

  async create(createUserDto: CreateUserDto) {
    await this.authRepository.save(createUserDto)
  }

  async generatePasswordSetupToken(userId: number): Promise<string> {
    // Generate JWT token valid for 5 minutes
    const payload = {
      user_id: userId,
      type: "password_setup",
    }

    // Use jwt.sign directly for custom expiration
    const token = jwt.sign(payload, EV["JWT_SECRET"], {
      expiresIn: "2d", // 2 days
    })

    // Store token in database
    const expiresAt = new Date(Date.now() + 5 * 60 * 1000) // 5 minutes from now
    await this.passwordSetupTokenRepository.save({
      user_id: userId,
      token: token,
      expires_at: expiresAt,
      is_used: false,
    })

    return token
  }

  async sendPasswordSetupEmail(
    userId: number,
    employeeName: string,
    email: string,
  ) {
    try {
      const token = await this.generatePasswordSetupToken(userId)

      const setupLink = `${EV["FRONT_URL"]}set-password/${token}`

      const emailContent = passwordSetupEmail(employeeName, setupLink)

      await sendEmailNotification(
        email.toLowerCase(),
        emailContent,
        mailSubject.passwordSetupSubject,
      )

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.email_sent),
      )
    } catch (error) {
      console.error("Error in sendPasswordSetupEmail:", error)
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.email_send_failed),
      )
    }
  }

  async setPassword(setPasswordDto: SetPasswordDto) {
    try {
      // Verify and decode token
      const decoded = verifyJwtToken(setPasswordDto.token)

      const tokenType = (decoded as any)?.type

      if (
        !decoded ||
        (tokenType !== "password_setup" && tokenType !== "email_change")
      ) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Check if token exists in database and is not used
      const tokenRecord = await this.passwordSetupTokenRepository.getByParams({
        where: {
          token: setPasswordDto.token,
          is_used: false,
        },
        findOne: true,
      })

      if (isEmpty(tokenRecord)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_or_expired_token),
        )
      }

      // Check if token is expired
      if (new Date() > (tokenRecord as any).expires_at) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.token_expired),
        )
      }

      // Get user
      const user = await this.authRepository.getByParams({
        where: { id: (decoded as any).user_id },
        findOne: true,
      })

      if (isEmpty(user)) {
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.data_not_found, { ":data": "User" }),
        )
      }

      // Update user password and activate account
      const hashedPassword = await encryptPassword(setPasswordDto.password)
      ;(user as any).password = hashedPassword

      if ((decoded as any)?.new_email) {
        ;(user as any).email = (decoded as any)?.new_email
      }

      ;(user as any).status = 1
      await this.authRepository.save(user as any)

      // Mark token as used
      ;(tokenRecord as any).is_used = true
      await this.passwordSetupTokenRepository.save(tokenRecord as any)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.password_set_successfully),
      )
    } catch (error) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }
  }

  async linkUserToEmployee(userId: number, employeeId: number) {
    const user = await this.authRepository.getByParams({
      where: { id: userId },
      findOne: true,
    })

    if (!isEmpty(user)) {
      ;(user as any).employee_id = employeeId
      await this.authRepository.save(user as any)
    }
  }

  async generateEmailChangeToken(
    userId: number,
    newEmail: string,
  ): Promise<string> {
    const payload = {
      user_id: userId,
      role_id: 0, // Dummy value for email change tokens
      new_email: newEmail,
      type: "email_change",
    }

    const token = jwt.sign(payload, process.env.JWT_SECRET, {
      expiresIn: "5m", // 5 minutes expiry
    })

    // Store token in database (reusing password_setup_tokens table for simplicity)
    await this.passwordSetupTokenRepository.save({
      user_id: userId,
      token: token,
      expires_at: moment().add(5, "minutes").toDate(),
      is_used: false,
    })

    return token
  }

  async sendEmailChangeRequestEmail(
    userId: number,
    userName: string,
    oldEmail: string,
    newEmail: string,
  ) {
    try {
      const token = await this.generateEmailChangeToken(userId, newEmail)
      const verificationLink = `${EV["FRONT_URL"]}set-password/${token}`

      const emailContent = emailChangeRequestTemplate(
        userName,
        oldEmail,
        newEmail,
        verificationLink,
      )

      await sendEmailNotification(
        newEmail.toLowerCase(),
        emailContent,
        mailSubject.emailChangeRequestSubject,
      )

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.email_sent),
      )
    } catch (error) {
      console.error("Error in sendEmailChangeRequestEmail:", error)
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.email_send_failed),
      )
    }
  }

  async getProfile(token: string) {
    const decoded = await this.verifyToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    const user: any = await this.authRepository.getByParams({
      where: { id: decoded.user_id },
      relations: ["role"], // <-- Add role relation
      findOne: true,
    })

    if (isEmpty(user) || !isEmpty(user.account_locked_at)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.user_not_found),
      )
    }

    let departmentName = null

    if (decoded.employee_id) {
      const employee: any = await this.employeeRepository.getByParams({
        where: { id: decoded.employee_id },
        relations: ["department"], // <-- Join Department
        findOne: true,
      })

      if (employee) {
        departmentName = employee.department?.name || null
      }
    }

    const userObj = {
      ...generateResponseObject(user, profileResponse),
      role_name: user.role?.name || null,
      department_name: departmentName,
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.profile_retrieve),
      userObj,
    )
  }

  async editProfile(
    token: string,
    editProfileDto: EditProfileDto,
    attachment: any,
  ) {
    const decoded = await this.verifyToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    const user: any = await this.authRepository.getByParams({
      where: {
        id: decoded.user_id,
      },
      findOne: true,
    })

    if (attachment) {
      const attachmentUrl = `${fileStoreLocation.profile_pic}/${attachment.filename}`
      user.profile_pic = attachmentUrl
    }

    user.first_name = editProfileDto.first_name
    user.last_name = editProfileDto.last_name
    user.phone = editProfileDto.contact_no

    await this.authRepository.save(user)

    if (user?.employee_id) {
      // update employee first name and last name and contact no

      const employee: any = await this.employeeRepository.getByParams({
        where: { id: user.employee_id },
        findOne: true,
      })

      if (employee) {
        employee.first_name = editProfileDto.first_name
        employee.last_name = editProfileDto.last_name
        employee.contact_no = editProfileDto.contact_no

        await this.employeeRepository.save(employee)
      }
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.profile_update),
    )
  }

  async changePassword(token: string, changePasswordDto: ChangePasswordDto) {
    const decoded = await this.verifyToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    const user: any = await this.authRepository.getByParams({
      where: {
        id: decoded.user_id,
      },
      findOne: true,
    })

    if (
      !(await this.comparePassword(
        changePasswordDto.old_password,
        user.password,
      ))
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.old_password_incorrect),
      )
    }

    const encryptedPassword = await encryptPassword(
      changePasswordDto.new_password,
    )

    const authUser = new Auth()
    authUser.password = encryptedPassword

    await this.authRepository.save(authUser, { id: user.id })

    await sendEmailNotification(
      user.email.toLowerCase(),
      changePasswordIntimation((user.first_name + " " + user.last_name).trim()),
      mailSubject.changePasswordSubject,
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.password_update),
    )
  }

  /**
   * Retrieves a user by their access token (JWT).
   *
   * @param {string} token - The JWT access token of the user.
   * @return {Promise<JwtPayload | null>} A Promise that resolves to the decoded JWT payload if valid, or null if invalid.
   */
  async getUserByToken(token: string): Promise<JwtPayload | null> {
    return await this.verifyToken(token)
  }

  private comparePassword = async (
    password: string,
    hashedPassword: string,
  ) => {
    return await bcrypt.compare(password, hashedPassword)
  }

  async logout(token: string) {
    const decoded = await this.verifyToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    await this.userBlacklistedTokenRepository.save({
      token: token,
      user_id: decoded.user_id,
      created_by: decoded.user_id,
    })

    // Find and delete the user login record by user_id
    const userLogin: any = await this.userLoginRepository.getByParams({
      where: {
        user_id: decoded.user_id,
      },
      findOne: true,
    })

    if (!isEmpty(userLogin)) {
      await this.userLoginRepository.remove(
        { id: userLogin.id },
        undefined,
        true,
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.logout_success),
    )
  }

  async checkAdminExist(email: string) {
    const user: any = await this.authRepository.getByParams({
      where: {
        email: email.toLowerCase(),
      },
    })

    return !isEmpty(user)
  }

  private async validateEmployeeDepartmentStatus(
    employeeId: number,
    roleId: number,
  ): Promise<void> {
    // Check if user is master admin - skip department validation
    const masterAdminRole =
      await this.roleService.findRoleByNameAndCompany("master_admin")
    if (masterAdminRole && masterAdminRole.id === roleId) {
      return // Master admin bypasses department check
    }

    // Fetch employee with department relation
    const employee: any = await this.employeeRepository.getByParams({
      where: { id: employeeId },
      relations: ["department"],
      findOne: true,
    })

    if (employee && employee.department) {
      // Check if department status is inactive (0)
      if (employee.department.status === 0) {
        throw new Error(
          JSON.stringify({
            status: false,
            code: 1007,
            message: "You cannot login because your department is inactive.",
          }),
        )
      }
    }
  }

  private async validateCompanySubscriptionStatus(
    roleId: number,
    companyId: number,
  ): Promise<void> {
    // Check if user is master admin - skip subscription validation
    const masterAdminRole =
      await this.roleService.findRoleByNameAndCompany("master_admin")
    if (masterAdminRole && masterAdminRole.id === roleId) {
      return // Master admin bypasses subscription check
    }

    // Fetch active company subscription
    const subscription: any =
      await this.companySubscriptionRepository.getByParams({
        where: {
          company_id: companyId,
          status: SubscriptionStatus.ACTIVE,
        },
        findOne: true,
      })

    if (!subscription) {
      throw new Error(
        JSON.stringify({
          status: false,
          code: 1008,
          message: "Company subscription not found. Please contact admin.",
        }),
      )
    }

    const currentDate = new Date()
    const subscriptionStartDate = new Date(subscription.subscription_start_date)

    if (subscriptionStartDate > currentDate) {
      throw new Error(
        JSON.stringify({
          status: false,
          code: 1008,
          message:
            "Company subscription has not started yet. Please contact admin.",
        }),
      )
    }

    // Check if subscription has expired based on end date
    const subscriptionEndDate = new Date(subscription.subscription_end_date)

    if (subscriptionEndDate < currentDate) {
      throw new Error(
        JSON.stringify({
          status: false,
          code: 1008,
          message: "Company subscription has expired. Please contact admin.",
        }),
      )
    }
  }

  async requestDemo(requestDemoDto: RequestDemoDto) {
    try {
      const adminEmail = EV["ADMIN_EMAIL"] || EV["SUPPORT_EMAIL"]

      if (!adminEmail) {
        console.error(
          "Admin/Support email not configured in environment variables",
        )
        return failureResponse(
          code.VALIDATION,
          errorMessage(messageKey.email_send_failed),
        )
      }

      const emailContent = demoRequestEmail(
        requestDemoDto.full_name,
        requestDemoDto.work_email,
        requestDemoDto.company_name,
        requestDemoDto.phone_number,
        requestDemoDto.message,
        requestDemoDto.plan_type,
      )

      await sendEmailNotification(
        adminEmail,
        emailContent,
        mailSubject.demoRequestSubject,
      )

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.demo_request_sent),
      )
    } catch (error) {
      console.error("Error in requestDemo:", error)
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.email_send_failed),
      )
    }
  }

  async verifyEmail(token: string) {
    try {
      if (!token) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.field_required, {
            ":field": "token",
          }),
        )
      }

      // Verify JWT token
      const decoded = verifyJwtToken(token)
      if (!decoded) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Find user by email verification token
      const user: any = await this.authRepository.getByParams({
        where: { email_verification_token: token },
        findOne: true,
      })

      if (!user) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.invalid_token),
        )
      }

      // Check if email is already verified
      if (user.is_email_verified) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.email_already_verified),
        )
      }

      // Update user to mark email as verified and activate account
      await this.authRepository.save({
        id: user.id,
        is_email_verified: true,
        status: 1, // Activate user
        email_verification_token: null, // Clear the token
      })

      return {
        success: true,
        code: code.SUCCESS,
        message: successMessage(messageKey.email_verified_successfully),
        data: {
          message:
            "Email verified successfully. You can now log in to your account.",
        },
      }
    } catch (error) {
      console.error("Error in verifyEmail:", error)
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.exception),
      )
    }
  }

  /**
   * Bulk send feature showcase emails from Excel file
   * Excel format: name, email columns (first row may contain notes, headers in second row)
   */
  async bulkSendFeatureEmails(fileBuffer: Buffer) {
    try {
      // Parse Excel file
      const emailData = this.parseEmailExcelFile(fileBuffer)

      // Get original headers for error Excel generation
      const workbook = XLSX.read(fileBuffer, { type: "buffer" })
      const sheetName = workbook.SheetNames[0]
      const worksheet = workbook.Sheets[sheetName]
      const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 })
      const originalHeaders = jsonData[1] as string[] // Use second row as headers (skip NOTE row)

      const response: BulkEmailResponse = {
        total_records: emailData.length,
        success_count: 0,
        failure_count: 0,
        errors: [],
      }

      const errorRowsForExcel: any[] = []

      // Process each email record
      for (const emailRecord of emailData) {
        const rowNumber = emailRecord.row_number
        const name = emailRecord.name || ""
        const email = emailRecord.email || ""

        try {
          // Validate email is required and valid
          if (!email) {
            throw new Error("Email is required")
          }

          // Basic email validation
          const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
          if (!emailRegex.test(email)) {
            throw new Error("Invalid email format")
          }

          // Send feature showcase email
          await sendEmailNotification(
            email,
            featuresShowcaseEmail(name),
            mailSubject.featuresShowcaseSubject,
            "",
            "",
            true,
          )

          response.success_count++
        } catch (error) {
          // Add error to response
          response.failure_count++
          const errorMessage = error.message || "Unknown error occurred"

          response.errors.push({
            row_number: rowNumber,
            name: name,
            email: email,
            error_message: errorMessage,
          })

          // Store error row data for Excel generation
          errorRowsForExcel.push({
            originalData: emailRecord,
            error_message: errorMessage,
          })
        }
      }

      // Generate error Excel file if there are errors
      if (errorRowsForExcel.length > 0) {
        response.error_file = this.generateEmailErrorExcel(
          errorRowsForExcel,
          originalHeaders,
        )
      }

      return successResponse(
        code.SUCCESS,
        `Bulk email sending completed. ${response.success_count} emails sent successfully, ${response.failure_count} failed.`,
        response as any,
      )
    } catch (error) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.exception, { ":error": error.message }),
      )
    }
  }

  /**
   * Parse Excel file for email data (name, email columns)
   */
  private parseEmailExcelFile(buffer: Buffer): any[] {
    try {
      const workbook = XLSX.read(buffer, { type: "buffer" })
      const sheetName = workbook.SheetNames[0]
      const worksheet = workbook.Sheets[sheetName]

      // Convert to JSON with header row as keys
      const jsonData = XLSX.utils.sheet_to_json(worksheet, {
        header: 1,
        defval: null,
      })

      if (jsonData.length < 3) {
        throw new Error(
          "Excel file must contain at least a note row, header row and one data row",
        )
      }

      // Skip the first row (NOTE row) and use the second row as headers
      const headers = jsonData[1] as string[]
      const rows = jsonData.slice(2) // Start from third row (data rows)

      // Map column names (case insensitive)
      const columnMapping: { [key: string]: string } = {
        name: "name",
        full_name: "name",
        "full name": "name",
        first_name: "name",
        firstname: "name",
        "first name": "name",
        email: "email",
        email_address: "email",
        "email address": "email",
      }

      // Create mapped headers
      const mappedHeaders = headers.map((header) => {
        const normalizedHeader = header?.toString().toLowerCase().trim()
        return columnMapping[normalizedHeader] || normalizedHeader
      })

      // Convert rows to objects
      const emailData = rows.map((row: any[], index: number) => {
        const emailRecord: any = { row_number: index + 3 } // +3 because index starts at 0, we skip note row and header row

        mappedHeaders.forEach((header, colIndex) => {
          const value = row[colIndex]
          if (value !== null && value !== undefined && value !== "") {
            emailRecord[header] = value.toString().trim()
          }
        })

        return emailRecord
      })

      // Filter out empty rows (rows without email)
      return emailData.filter((record) => record.email)
    } catch (error) {
      throw new Error(`Failed to parse Excel file: ${error.message}`)
    }
  }

  /**
   * Generate Excel file with error rows for email bulk sending
   */
  private generateEmailErrorExcel(
    errorRows: any[],
    originalHeaders: string[],
  ): Buffer {
    try {
      // Add error_message column to headers
      const headers = [...originalHeaders, "error_message"]

      // Create worksheet data
      const worksheetData = [headers]

      // Add error rows with error messages
      errorRows.forEach((errorRow) => {
        const row = []
        originalHeaders.forEach((header) => {
          // Map the header to the correct field name
          const normalizedHeader = header?.toString().toLowerCase().trim()
          const columnMapping: { [key: string]: string } = {
            name: "name",
            full_name: "name",
            "full name": "name",
            first_name: "name",
            firstname: "name",
            "first name": "name",
            email: "email",
            email_address: "email",
            "email address": "email",
          }
          const mappedHeader =
            columnMapping[normalizedHeader] || normalizedHeader
          row.push(errorRow.originalData[mappedHeader] || "")
        })
        row.push(errorRow.error_message)
        worksheetData.push(row)
      })

      // Create workbook and worksheet
      const workbook = XLSX.utils.book_new()
      const worksheet = XLSX.utils.aoa_to_sheet(worksheetData)

      // Add worksheet to workbook
      XLSX.utils.book_append_sheet(workbook, worksheet, "Email_Errors")

      // Generate buffer
      return XLSX.write(workbook, { type: "buffer", bookType: "xlsx" })
    } catch (error) {
      throw new Error(`Failed to generate error Excel file: ${error.message}`)
    }
  }
}
