import type {
  CollectionAfterChangeHook,
  CollectionAfterOperationHook,
  CollectionBeforeChangeHook,
  CollectionBeforeOperationHook,
  PayloadRequest,
} from 'payload'

import type { User } from '@/payload-types'

const PASSWORD_CHANGE_ALERT_CONTEXT_KEY = 'sendPasswordChangeAlert'
const PASSWORD_CHANGE_ALERT_EMAIL = process.env.PASSWORD_CHANGE_ALERT_EMAIL

type PasswordAlertContext = {
  [PASSWORD_CHANGE_ALERT_CONTEXT_KEY]?: boolean
}

const getPasswordAlertContext = (context: unknown): PasswordAlertContext =>
  (context as PasswordAlertContext | undefined) ?? {}

const isUser = (value: unknown): value is User => {
  if (!value || typeof value !== 'object') return false

  const candidate = value as Partial<User>

  return (
    typeof candidate.id === 'number' &&
    typeof candidate.email === 'string' &&
    typeof candidate.updatedAt === 'string' &&
    typeof candidate.createdAt === 'string' &&
    Array.isArray(candidate.roles)
  )
}

const formatActor = (user: User | null | undefined) => {
  if (!user) return 'Self-service or unauthenticated flow'

  const name = user.name?.trim()
  return name ? `${name} (${user.email})` : user.email
}

const buildEmailHtml = ({
  actor,
  operation,
  targetUser,
}: {
  actor: string
  operation: 'create' | 'update' | 'resetPassword'
  targetUser: User
}) => {
  return `
    <h2>Password Change Alert</h2>
    <p>A user password was updated in the Marketing CMS.</p>
    <table cellpadding="6" cellspacing="0" border="1" style="border-collapse: collapse;">
      <tr><td><strong>Operation</strong></td><td>${operation}</td></tr>
      <tr><td><strong>User ID</strong></td><td>${targetUser.id}</td></tr>
      <tr><td><strong>Name</strong></td><td>${targetUser.name ?? 'N/A'}</td></tr>
      <tr><td><strong>Email</strong></td><td>${targetUser.email}</td></tr>
      <tr><td><strong>Updated At</strong></td><td>${targetUser.updatedAt}</td></tr>
      <tr><td><strong>Triggered By</strong></td><td>${actor}</td></tr>
    </table>
  `
}

const buildEmailText = ({
  actor,
  operation,
  targetUser,
}: {
  actor: string
  operation: 'create' | 'update' | 'resetPassword'
  targetUser: User
}) => {
  return [
    'Password Change Alert',
    '',
    'A user password was updated in the Marketing CMS.',
    `Operation: ${operation}`,
    `User ID: ${targetUser.id}`,
    `Name: ${targetUser.name ?? 'N/A'}`,
    `Email: ${targetUser.email}`,
    `Updated At: ${targetUser.updatedAt}`,
    `Triggered By: ${actor}`,
  ].join('\n')
}

const sendPasswordAlertEmail = async ({
  actor,
  operation,
  req,
  targetUser,
}: {
  actor: string
  operation: 'create' | 'update' | 'resetPassword'
  req: PayloadRequest
  targetUser: User
}) => {
  if (!PASSWORD_CHANGE_ALERT_EMAIL) {
    req.payload.logger.warn(
      'Skipping password change alert because PASSWORD_CHANGE_ALERT_EMAIL is not configured.',
    )
    return
  }

  try {
    await req.payload.sendEmail({
      to: PASSWORD_CHANGE_ALERT_EMAIL,
      subject: `Password changed for ${targetUser.email}`,
      text: buildEmailText({
        actor,
        operation,
        targetUser,
      }),
      html: buildEmailHtml({
        actor,
        operation,
        targetUser,
      }),
    })
  } catch (error) {
    req.payload.logger.error({
      err: error,
      msg: `Failed to send password change alert for user ${targetUser.email}`,
    })
  }
}

export const markPasswordChangeForAlert: CollectionBeforeChangeHook<User> = async ({
  context,
  data,
  operation,
}) => {
  const password = data?.password
  const isPasswordUpdate = operation === 'update' && typeof password === 'string' && password.length > 0

  getPasswordAlertContext(context)[PASSWORD_CHANGE_ALERT_CONTEXT_KEY] = isPasswordUpdate

  return data
}

export const markResetPasswordForAlert: CollectionBeforeOperationHook<'users'> = async (args) => {
  if (args.operation !== 'resetPassword') return args.args

  const nextContext = getPasswordAlertContext(args.req.context)
  const password = args.args?.data?.password

  nextContext[PASSWORD_CHANGE_ALERT_CONTEXT_KEY] =
    typeof password === 'string' && password.length > 0

  return args.args
}

export const sendPasswordChangeAlert: CollectionAfterChangeHook<User> = async ({
  context,
  doc,
  operation,
  req,
}) => {
  const shouldSendAlert = getPasswordAlertContext(context)[PASSWORD_CHANGE_ALERT_CONTEXT_KEY]

  if (!shouldSendAlert) return doc

  await sendPasswordAlertEmail({
    actor: formatActor(req.user as User | null | undefined),
    operation,
    req,
    targetUser: doc,
  })

  return doc
}

export const sendResetPasswordAlert: CollectionAfterOperationHook<'users'> = async (args) => {
  if (args.operation !== 'resetPassword') return args.result

  const shouldSendAlert = getPasswordAlertContext(args.req.context)[PASSWORD_CHANGE_ALERT_CONTEXT_KEY]
  const targetUser = args.result?.user

  if (!shouldSendAlert || !isUser(targetUser)) return args.result

  await sendPasswordAlertEmail({
    actor: formatActor(args.req.user as User | null | undefined),
    operation: 'resetPassword',
    req: args.req,
    targetUser,
  })

  return args.result
}
