import { Types } from 'mongoose';

import {
  PermissionAction,
  Status,
  UserType,
} from '@/shared/constants/enum.constant';
import { IPermissionDoc } from '@/modules/permissions/permissions.interface';
import { Role } from '@/modules/roles/roles.model';
import { IUserDoc } from '@/modules/user/user.interfaces';
import {
  AuthenticatedUserResponse,
  SanitizedUser,
  SetPasswordEmailData,
  SetPasswordEmailResult,
} from '@/modules/auth/auth.interfaces';
import { TemplateName, CONSTANTS } from '@/shared/constants';
import { generateResetPasswordToken } from '@/modules/token/token.service';
import config from '@/shared/config/config';
import { loadEmailTemplateFromFile } from '@/shared/email/email.util';
import { ApiError } from '@/shared/utils/errors';
import { defaultStatus } from '@/shared/utils/responseCode/httpStatusAlias';
import User from '@/modules/user/user.model';
import responseCodes from '@/shared/utils/responseCode/responseCode';
import { sendEmailWithActiveTemplate } from '../communication/email/email.helper';

const { UserResponseCodes } = responseCodes;

function sanitizeUser(userDoc: IUserDoc): SanitizedUser {
  const obj = userDoc.toObject();
  const { password, __v, company, subCompany, _id, ...rest } = obj;

  const {
    _id: compId,
    name: companyName,
    companyType,
    country,
    state,
    city,
    area,
    pincode,
  } = company.id as {
    _id: Types.ObjectId;
    name: string;
    companyType: string;
    country: Types.ObjectId;
    state: Types.ObjectId;
    city: Types.ObjectId;
    area: Types.ObjectId;
    pincode: number;
  };

  return {
    id: String(_id),
    ...rest,
    companyId: String(compId),
    companyName,
    companyType,
    roleIds: (company.role || []).map((r: Types.ObjectId) => String(r)),
    subCompanyRoleIds: (subCompany.role || []).map((r: Types.ObjectId) =>
      String(r),
    ),

    ...(country && { country }),
    ...(state && { state }),
    ...(city && { city }),
    ...(area && { area }),
    ...(pincode && { pincode }),
  };
}

export async function buildAuthResponse(
  userDoc: IUserDoc,
): Promise<AuthenticatedUserResponse> {
  const companyRoles = userDoc.company?.role || [];
  const permSet = new Set<string>();

  if (companyRoles.length) {
    const rolesWithPerms = await Role.find({
      _id: { $in: companyRoles },
    }).populate<{ permissions: IPermissionDoc[] }>('permissions');

    for (const role of rolesWithPerms)
      for (const grp of role.permissions || [])
        for (const modPerm of grp.permissions) {
          const actions =
            grp.groupAccess === CONSTANTS.FULL_ACCESS
              ? PermissionAction
              : Array.isArray(modPerm.access)
                ? modPerm.access
                : [];

          for (const act of actions) permSet.add(`${modPerm.id}:${act}`);
        }
  }

  const permissions = Array.from(permSet);

  const user = sanitizeUser(userDoc);

  return { user, permissions };
}

export const generateSetPasswordEmail = async ({
  user,
  company,
  type,
}: SetPasswordEmailData): Promise<SetPasswordEmailResult> => {
  const token = await generateResetPasswordToken(user.email);

  const setPasswordLink = `${config.resetPasswordUrl}?token=${token}`;

  const templateName =
    type === 'resetPassword' ? 'reset-password' : 'set-password';

  const emailContent = loadEmailTemplateFromFile(templateName, {
    userName: `${user.firstName} ${user.lastName}`,
    companyName: user.userType === UserType.CRMUSER ? company.name : 'Makanify',
    setPasswordLink,
  });

  return {
    setPasswordLink,
    emailContent,
    token,
  };
};

export async function sendSetPasswordEmail({
  user,
  company,
  createdBy,
  type,
}): Promise<boolean> {
  console.log('🚀 ~ sendSetPasswordEmail ~ type:', type);
  try {
    const { setPasswordLink } = await generateSetPasswordEmail({
      user: {
        id: user.id as Types.ObjectId,
        firstName: user.firstName,
        lastName: user.lastName,
        userType: user.userType,
        email: user.email,
      },
      company: {
        id: company.id as Types.ObjectId,
        name: company.name,
      },
      type,
    });

    await sendEmailWithActiveTemplate({
      to: user.email,
      companyId: company.id as Types.ObjectId,
      scenario: TemplateName.EmailVerification,
      templateParams: {
        fullName: `${user.firstName} ${user.lastName}`,
        adminName: `${createdBy.firstName} ${createdBy.lastName}`,
        companyName: company.name,
        hostUrl: setPasswordLink,
      },
    });

    // sendEmail(
    //   user.email,
    //   '🔐 Set Your Password to Activate Your Account',
    //   emailContent,
    // ).catch((err) => {
    //   console.error('Background sendEmail failed:', err);
    //   // can manage queue here
    // });

    return true;
  } catch (_err) {
    return false;
  }
}

/**
 * Validates user token against permission updates
 * @param payload - JWT payload with sub (userId) and iat (issued at)
 * @returns Promise<User> - Returns user if valid, throws ApiError if invalid
 */
export const validateUserToken = async (payload: {
  sub: string;
  iat: number;
  resetTokenVersion?: number;
}) => {
  const user = await User.findById(payload.sub).select(
    '+lastPermissionUpdate +resetTokenVersion +passwordChangedAt',
  );

  if (!user)
    throw new ApiError(
      defaultStatus.OK,
      'User not found',
      true,
      '',
      UserResponseCodes.TOKEN_INVALID,
    );

  const tokenIssuedAt = new Date(payload.iat * 1000);
  const lastPermissionUpdate = new Date(user.lastPermissionUpdate);

  if (user.status === Status.INACTIVE)
    throw new ApiError(
      defaultStatus.OK,
      'Your account is got inactivated. Please contact support.',
      true,
      '',
      UserResponseCodes.USER_INACTIVE,
    );

  if (
    !isNaN(lastPermissionUpdate.getTime()) &&
    tokenIssuedAt < lastPermissionUpdate
  )
    throw new ApiError(
      defaultStatus.OK,
      'Token expired due to permission changes',
      true,
      '',
      UserResponseCodes.TOKEN_INVALID,
    );

  if (
    payload.resetTokenVersion !== undefined &&
    payload.resetTokenVersion !== user.resetTokenVersion
  )
    throw new ApiError(
      defaultStatus.OK,
      'Invalid token',
      true,
      '',
      UserResponseCodes.TOKEN_INVALID,
    );

  return user;
};

export const sendUserEmail = async (user, company) => {
  const commonParams = {
    fullName: `${user.firstName} ${user.lastName}`,
    loginUrl: `${config.clientUrl}${config.loginRoute}`,
  };

  const scenarioName = user.isEmailVerified
    ? TemplateName.ResetPassword
    : TemplateName.AccountActivated;

  const params = user.isEmailVerified
    ? commonParams
    : {
        ...commonParams,
        companyName: company.name,
        teamName: user?.team?.[0]?.name,
      };

  await sendEmailWithActiveTemplate({
    to: user.email,
    companyId: company.id,
    scenario: scenarioName,
    templateParams: params,
  });
};
