import crypto from 'crypto'
import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import type { Prisma } from '@prisma/client'
import { router, protectedProcedure, adminProcedure, superAdminProcedure, publicProcedure } from '../trpc'
import { sendInvitationEmail, sendMagicLinkEmail } from '@/lib/email'
import { hashPassword, validatePassword } from '@/lib/password'
import { attachAvatarUrls } from '@/server/utils/avatar-url'

const INVITE_TOKEN_EXPIRY_MS = 7 * 24 * 60 * 60 * 1000 // 7 days

function generateInviteToken(): string {
  return crypto.randomBytes(32).toString('hex')
}

export const userRouter = router({
  /**
   * Get current user profile
   */
  me: protectedProcedure.query(async ({ ctx }) => {
    return ctx.prisma.user.findUniqueOrThrow({
      where: { id: ctx.user.id },
      select: {
        id: true,
        email: true,
        name: true,
        role: true,
        status: true,
        expertiseTags: true,
        metadataJson: true,
        phoneNumber: true,
        country: true,
        notificationPreference: true,
        profileImageKey: true,
        createdAt: true,
        lastLoginAt: true,
      },
    })
  }),

  /**
   * Validate an invitation token (public, no auth required)
   */
  validateInviteToken: publicProcedure
    .input(z.object({ token: z.string().min(1) }))
    .query(async ({ ctx, input }) => {
      const user = await ctx.prisma.user.findUnique({
        where: { inviteToken: input.token },
        select: { id: true, name: true, email: true, role: true, status: true, inviteTokenExpiresAt: true },
      })

      if (!user) {
        return { valid: false, error: 'INVALID_TOKEN' as const }
      }

      if (user.status !== 'INVITED') {
        return { valid: false, error: 'ALREADY_ACCEPTED' as const }
      }

      if (user.inviteTokenExpiresAt && user.inviteTokenExpiresAt < new Date()) {
        return { valid: false, error: 'EXPIRED_TOKEN' as const }
      }

      return {
        valid: true,
        user: { name: user.name, email: user.email, role: user.role },
      }
    }),

  /**
   * Update current user profile
   */
  updateProfile: protectedProcedure
    .input(
      z.object({
        name: z.string().min(1).max(255).optional(),
        bio: z.string().max(1000).optional(),
        phoneNumber: z.string().max(20).optional().nullable(),
        notificationPreference: z.enum(['EMAIL', 'WHATSAPP', 'BOTH', 'NONE']).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { bio, ...directFields } = input

      // If bio is provided, merge it into metadataJson
      let metadataJson: Prisma.InputJsonValue | undefined
      if (bio !== undefined) {
        const currentUser = await ctx.prisma.user.findUniqueOrThrow({
          where: { id: ctx.user.id },
          select: { metadataJson: true },
        })
        const currentMeta = (currentUser.metadataJson as Record<string, string>) || {}
        metadataJson = { ...currentMeta, bio } as Prisma.InputJsonValue
      }

      return ctx.prisma.user.update({
        where: { id: ctx.user.id },
        data: {
          ...directFields,
          ...(metadataJson !== undefined && { metadataJson }),
        },
      })
    }),

  /**
   * Delete own account (requires password confirmation)
   */
  deleteAccount: protectedProcedure
    .input(
      z.object({
        password: z.string().min(1),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get current user with password hash
      const user = await ctx.prisma.user.findUniqueOrThrow({
        where: { id: ctx.user.id },
        select: { id: true, email: true, passwordHash: true, role: true },
      })

      // Prevent super admins from self-deleting
      if (user.role === 'SUPER_ADMIN') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Super admins cannot delete their own account',
        })
      }

      if (!user.passwordHash) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'No password set. Please set a password first.',
        })
      }

      // Verify password
      const { verifyPassword } = await import('@/lib/password')
      const isValid = await verifyPassword(input.password, user.passwordHash)
      if (!isValid) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'Password is incorrect',
        })
      }

      // Audit log before deletion
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE_OWN_ACCOUNT',
          entityType: 'User',
          entityId: ctx.user.id,
          detailsJson: { email: user.email },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      // Delete the user
      await ctx.prisma.user.delete({
        where: { id: ctx.user.id },
      })

      return { success: true }
    }),

  /**
   * List all users (admin only)
   */
  list: adminProcedure
    .input(
      z.object({
        role: z.enum(['SUPER_ADMIN', 'PROGRAM_ADMIN', 'JURY_MEMBER', 'MENTOR', 'OBSERVER']).optional(),
        roles: z.array(z.enum(['SUPER_ADMIN', 'PROGRAM_ADMIN', 'JURY_MEMBER', 'MENTOR', 'OBSERVER'])).optional(),
        status: z.enum(['INVITED', 'ACTIVE', 'SUSPENDED']).optional(),
        search: z.string().optional(),
        page: z.number().int().min(1).default(1),
        perPage: z.number().int().min(1).max(100).default(20),
      })
    )
    .query(async ({ ctx, input }) => {
      const { role, roles, status, search, page, perPage } = input
      const skip = (page - 1) * perPage

      const where: Record<string, unknown> = {}

      if (roles && roles.length > 0) {
        where.role = { in: roles }
      } else if (role) {
        where.role = role
      }
      if (status) where.status = status
      if (search) {
        where.OR = [
          { email: { contains: search, mode: 'insensitive' } },
          { name: { contains: search, mode: 'insensitive' } },
        ]
      }

      const [users, total] = await Promise.all([
        ctx.prisma.user.findMany({
          where,
          skip,
          take: perPage,
          orderBy: { createdAt: 'desc' },
          select: {
            id: true,
            email: true,
            name: true,
            role: true,
            status: true,
            expertiseTags: true,
            maxAssignments: true,
            profileImageKey: true,
            profileImageProvider: true,
            createdAt: true,
            lastLoginAt: true,
            _count: {
              select: { assignments: true, mentorAssignments: true },
            },
          },
        }),
        ctx.prisma.user.count({ where }),
      ])

      const usersWithAvatars = await attachAvatarUrls(users)

      return {
        users: usersWithAvatars,
        total,
        page,
        perPage,
        totalPages: Math.ceil(total / perPage),
      }
    }),

  /**
   * Get a single user (admin only)
   */
  get: adminProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ ctx, input }) => {
      return ctx.prisma.user.findUniqueOrThrow({
        where: { id: input.id },
        include: {
          _count: {
            select: { assignments: true, mentorAssignments: true },
          },
        },
      })
    }),

  /**
   * Create/invite a new user (admin only)
   */
  create: adminProcedure
    .input(
      z.object({
        email: z.string().email(),
        name: z.string().optional(),
        role: z.enum(['PROGRAM_ADMIN', 'JURY_MEMBER', 'MENTOR', 'OBSERVER']).default('JURY_MEMBER'),
        expertiseTags: z.array(z.string()).optional(),
        maxAssignments: z.number().int().min(1).max(100).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Check if user already exists
      const existing = await ctx.prisma.user.findUnique({
        where: { email: input.email },
      })

      if (existing) {
        throw new TRPCError({
          code: 'CONFLICT',
          message: 'A user with this email already exists',
        })
      }

      // Prevent non-super-admins from creating admins
      if (input.role === 'PROGRAM_ADMIN' && ctx.user.role !== 'SUPER_ADMIN') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only super admins can create program admins',
        })
      }

      const user = await ctx.prisma.user.create({
        data: {
          ...input,
          status: 'INVITED',
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'CREATE',
          entityType: 'User',
          entityId: user.id,
          detailsJson: { email: input.email, role: input.role },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return user
    }),

  /**
   * Update a user (admin only)
   */
  update: adminProcedure
    .input(
      z.object({
        id: z.string(),
        name: z.string().optional().nullable(),
        role: z.enum(['PROGRAM_ADMIN', 'JURY_MEMBER', 'MENTOR', 'OBSERVER']).optional(),
        status: z.enum(['INVITED', 'ACTIVE', 'SUSPENDED']).optional(),
        expertiseTags: z.array(z.string()).optional(),
        maxAssignments: z.number().int().min(1).max(100).optional().nullable(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input

      // Prevent changing super admin role
      const targetUser = await ctx.prisma.user.findUniqueOrThrow({
        where: { id },
      })

      if (targetUser.role === 'SUPER_ADMIN' && ctx.user.role !== 'SUPER_ADMIN') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Cannot modify super admin',
        })
      }

      // Prevent non-super-admins from assigning admin role
      if (data.role === 'PROGRAM_ADMIN' && ctx.user.role !== 'SUPER_ADMIN') {
        throw new TRPCError({
          code: 'FORBIDDEN',
          message: 'Only super admins can assign admin role',
        })
      }

      const user = await ctx.prisma.user.update({
        where: { id },
        data,
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'UPDATE',
          entityType: 'User',
          entityId: id,
          detailsJson: data,
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      // Track role change specifically
      if (data.role && data.role !== targetUser.role) {
        await ctx.prisma.auditLog.create({
          data: {
            userId: ctx.user.id,
            action: 'ROLE_CHANGED',
            entityType: 'User',
            entityId: id,
            detailsJson: { previousRole: targetUser.role, newRole: data.role },
            ipAddress: ctx.ip,
            userAgent: ctx.userAgent,
          },
        }).catch(() => {})
      }

      return user
    }),

  /**
   * Delete a user (super admin only)
   */
  delete: superAdminProcedure
    .input(z.object({ id: z.string() }))
    .mutation(async ({ ctx, input }) => {
      // Prevent self-deletion
      if (input.id === ctx.user.id) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Cannot delete yourself',
        })
      }

      const user = await ctx.prisma.user.delete({
        where: { id: input.id },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'DELETE',
          entityType: 'User',
          entityId: input.id,
          detailsJson: { email: user.email },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return user
    }),

  /**
   * Bulk import users (admin only)
   * Optionally pre-assign projects to jury members during invitation
   */
  bulkCreate: adminProcedure
    .input(
      z.object({
        users: z.array(
          z.object({
            email: z.string().email(),
            name: z.string().optional(),
            role: z.enum(['JURY_MEMBER', 'MENTOR', 'OBSERVER']).default('JURY_MEMBER'),
            expertiseTags: z.array(z.string()).optional(),
            // Optional pre-assignments for jury members
            assignments: z
              .array(
                z.object({
                  projectId: z.string(),
                  roundId: z.string(),
                })
              )
              .optional(),
          })
        ),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Deduplicate input by email (keep first occurrence)
      const seenEmails = new Set<string>()
      const uniqueUsers = input.users.filter((u) => {
        const email = u.email.toLowerCase()
        if (seenEmails.has(email)) return false
        seenEmails.add(email)
        return true
      })

      // Get existing emails from database
      const existingUsers = await ctx.prisma.user.findMany({
        where: { email: { in: uniqueUsers.map((u) => u.email.toLowerCase()) } },
        select: { email: true },
      })
      const existingEmails = new Set(existingUsers.map((u) => u.email.toLowerCase()))

      // Filter out existing users
      const newUsers = uniqueUsers.filter((u) => !existingEmails.has(u.email.toLowerCase()))

      const duplicatesInInput = input.users.length - uniqueUsers.length
      const skipped = existingEmails.size + duplicatesInInput

      if (newUsers.length === 0) {
        return { created: 0, skipped }
      }

      // Build map of email -> assignments before createMany (since createMany removes extra fields)
      const emailToAssignments = new Map<string, Array<{ projectId: string; roundId: string }>>()
      for (const u of newUsers) {
        if (u.assignments && u.assignments.length > 0) {
          emailToAssignments.set(u.email.toLowerCase(), u.assignments)
        }
      }

      const created = await ctx.prisma.user.createMany({
        data: newUsers.map((u) => ({
          email: u.email.toLowerCase(),
          name: u.name,
          role: u.role,
          expertiseTags: u.expertiseTags,
          status: 'INVITED',
        })),
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'BULK_CREATE',
          entityType: 'User',
          detailsJson: { count: created.count, skipped, duplicatesInInput },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      // Auto-send invitation emails to newly created users
      const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000'
      const createdUsers = await ctx.prisma.user.findMany({
        where: { email: { in: newUsers.map((u) => u.email.toLowerCase()) } },
        select: { id: true, email: true, name: true, role: true },
      })

      // Create pre-assignments for users who have them
      let assignmentsCreated = 0
      for (const user of createdUsers) {
        const assignments = emailToAssignments.get(user.email.toLowerCase())
        if (assignments && assignments.length > 0) {
          for (const assignment of assignments) {
            try {
              await ctx.prisma.assignment.create({
                data: {
                  userId: user.id,
                  projectId: assignment.projectId,
                  roundId: assignment.roundId,
                  method: 'MANUAL',
                  createdBy: ctx.user.id,
                },
              })
              assignmentsCreated++
            } catch {
              // Skip if assignment already exists (shouldn't happen for new users)
            }
          }
        }
      }

      // Audit log for assignments if any were created
      if (assignmentsCreated > 0) {
        await ctx.prisma.auditLog.create({
          data: {
            userId: ctx.user.id,
            action: 'BULK_ASSIGN',
            entityType: 'Assignment',
            detailsJson: { count: assignmentsCreated, context: 'invitation_pre_assignment' },
            ipAddress: ctx.ip,
            userAgent: ctx.userAgent,
          },
        })
      }

      let emailsSent = 0
      const emailErrors: string[] = []

      for (const user of createdUsers) {
        try {
          const token = generateInviteToken()
          await ctx.prisma.user.update({
            where: { id: user.id },
            data: {
              inviteToken: token,
              inviteTokenExpiresAt: new Date(Date.now() + INVITE_TOKEN_EXPIRY_MS),
            },
          })

          const inviteUrl = `${baseUrl}/accept-invite?token=${token}`
          await sendInvitationEmail(user.email, user.name, inviteUrl, user.role)

          await ctx.prisma.notificationLog.create({
            data: {
              userId: user.id,
              channel: 'EMAIL',
              provider: 'SMTP',
              type: 'JURY_INVITATION',
              status: 'SENT',
            },
          })
          emailsSent++
        } catch (e) {
          emailErrors.push(user.email)
          await ctx.prisma.notificationLog.create({
            data: {
              userId: user.id,
              channel: 'EMAIL',
              provider: 'SMTP',
              type: 'JURY_INVITATION',
              status: 'FAILED',
              errorMsg: e instanceof Error ? e.message : 'Unknown error',
            },
          })
        }
      }

      return { created: created.count, skipped, emailsSent, emailErrors, assignmentsCreated }
    }),

  /**
   * Get jury members for assignment
   */
  getJuryMembers: adminProcedure
    .input(
      z.object({
        roundId: z.string().optional(),
        search: z.string().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const where: Record<string, unknown> = {
        role: 'JURY_MEMBER',
        status: 'ACTIVE',
      }

      if (input.search) {
        where.OR = [
          { email: { contains: input.search, mode: 'insensitive' } },
          { name: { contains: input.search, mode: 'insensitive' } },
        ]
      }

      const users = await ctx.prisma.user.findMany({
        where,
        select: {
          id: true,
          email: true,
          name: true,
          expertiseTags: true,
          maxAssignments: true,
          profileImageKey: true,
          profileImageProvider: true,
          _count: {
            select: {
              assignments: input.roundId
                ? { where: { roundId: input.roundId } }
                : true,
            },
          },
        },
        orderBy: { name: 'asc' },
      })

      const mapped = users.map((u) => ({
        ...u,
        currentAssignments: u._count.assignments,
        availableSlots:
          u.maxAssignments !== null
            ? Math.max(0, u.maxAssignments - u._count.assignments)
            : null,
      }))

      return attachAvatarUrls(mapped)
    }),

  /**
   * Send invitation email to a user
   */
  sendInvitation: adminProcedure
    .input(z.object({ userId: z.string() }))
    .mutation(async ({ ctx, input }) => {
      const user = await ctx.prisma.user.findUniqueOrThrow({
        where: { id: input.userId },
      })

      if (user.status !== 'INVITED') {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'User has already accepted their invitation',
        })
      }

      // Generate invite token and store on user
      const token = generateInviteToken()
      await ctx.prisma.user.update({
        where: { id: user.id },
        data: {
          inviteToken: token,
          inviteTokenExpiresAt: new Date(Date.now() + INVITE_TOKEN_EXPIRY_MS),
        },
      })

      const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000'
      const inviteUrl = `${baseUrl}/accept-invite?token=${token}`

      // Send invitation email
      await sendInvitationEmail(user.email, user.name, inviteUrl, user.role)

      // Log notification
      await ctx.prisma.notificationLog.create({
        data: {
          userId: user.id,
          channel: 'EMAIL',
          provider: 'SMTP',
          type: 'JURY_INVITATION',
          status: 'SENT',
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'SEND_INVITATION',
          entityType: 'User',
          entityId: user.id,
          detailsJson: { email: user.email },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { success: true, email: user.email }
    }),

  /**
   * Send invitation emails to multiple users
   */
  bulkSendInvitations: adminProcedure
    .input(z.object({ userIds: z.array(z.string()) }))
    .mutation(async ({ ctx, input }) => {
      const users = await ctx.prisma.user.findMany({
        where: {
          id: { in: input.userIds },
          status: 'INVITED',
        },
      })

      if (users.length === 0) {
        return { sent: 0, skipped: input.userIds.length }
      }

      const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000'
      let sent = 0
      const errors: string[] = []

      for (const user of users) {
        try {
          // Generate invite token for each user
          const token = generateInviteToken()
          await ctx.prisma.user.update({
            where: { id: user.id },
            data: {
              inviteToken: token,
              inviteTokenExpiresAt: new Date(Date.now() + INVITE_TOKEN_EXPIRY_MS),
            },
          })

          const inviteUrl = `${baseUrl}/accept-invite?token=${token}`
          await sendInvitationEmail(user.email, user.name, inviteUrl, user.role)

          await ctx.prisma.notificationLog.create({
            data: {
              userId: user.id,
              channel: 'EMAIL',
              provider: 'SMTP',
              type: 'JURY_INVITATION',
              status: 'SENT',
            },
          })

          sent++
        } catch (e) {
          errors.push(user.email)
          await ctx.prisma.notificationLog.create({
            data: {
              userId: user.id,
              channel: 'EMAIL',
              provider: 'SMTP',
              type: 'JURY_INVITATION',
              status: 'FAILED',
              errorMsg: e instanceof Error ? e.message : 'Unknown error',
            },
          })
        }
      }

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'BULK_SEND_INVITATIONS',
          entityType: 'User',
          detailsJson: { sent, errors },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { sent, skipped: input.userIds.length - users.length, errors }
    }),

  /**
   * Complete onboarding for current user
   */
  completeOnboarding: protectedProcedure
    .input(
      z.object({
        name: z.string().min(1).max(255),
        phoneNumber: z.string().optional(),
        country: z.string().optional(),
        expertiseTags: z.array(z.string()).optional(),
        notificationPreference: z.enum(['EMAIL', 'WHATSAPP', 'BOTH', 'NONE']).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get existing user to preserve admin-set tags
      const existingUser = await ctx.prisma.user.findUniqueOrThrow({
        where: { id: ctx.user.id },
        select: { expertiseTags: true },
      })

      // Merge admin-set tags with user-selected tags (preserving order: admin first, then user)
      const adminTags = existingUser.expertiseTags || []
      const userTags = input.expertiseTags || []
      const mergedTags = [...new Set([...adminTags, ...userTags])]

      const user = await ctx.prisma.user.update({
        where: { id: ctx.user.id },
        data: {
          name: input.name,
          phoneNumber: input.phoneNumber,
          country: input.country,
          expertiseTags: mergedTags,
          notificationPreference: input.notificationPreference || 'EMAIL',
          onboardingCompletedAt: new Date(),
          status: 'ACTIVE', // Activate user after onboarding
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'COMPLETE_ONBOARDING',
          entityType: 'User',
          entityId: ctx.user.id,
          detailsJson: { name: input.name },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return user
    }),

  /**
   * Check if current user needs onboarding
   */
  needsOnboarding: protectedProcedure.query(async ({ ctx }) => {
    const user = await ctx.prisma.user.findUniqueOrThrow({
      where: { id: ctx.user.id },
      select: { onboardingCompletedAt: true, role: true },
    })

    // Jury members, mentors, and admins need onboarding
    const rolesRequiringOnboarding = ['JURY_MEMBER', 'MENTOR', 'PROGRAM_ADMIN', 'SUPER_ADMIN']
    if (!rolesRequiringOnboarding.includes(user.role)) {
      return false
    }

    return user.onboardingCompletedAt === null
  }),

  /**
   * Check if current user needs to set a password
   */
  needsPasswordSetup: protectedProcedure.query(async ({ ctx }) => {
    const user = await ctx.prisma.user.findUniqueOrThrow({
      where: { id: ctx.user.id },
      select: { mustSetPassword: true, passwordHash: true },
    })

    return user.mustSetPassword || user.passwordHash === null
  }),

  /**
   * Set password for current user
   */
  setPassword: protectedProcedure
    .input(
      z.object({
        password: z.string().min(8),
        confirmPassword: z.string().min(8),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Validate passwords match
      if (input.password !== input.confirmPassword) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'Passwords do not match',
        })
      }

      // Validate password requirements
      const validation = validatePassword(input.password)
      if (!validation.valid) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: validation.errors.join('. '),
        })
      }

      // Hash the password
      const passwordHash = await hashPassword(input.password)

      // Update user with new password
      const user = await ctx.prisma.user.update({
        where: { id: ctx.user.id },
        data: {
          passwordHash,
          passwordSetAt: new Date(),
          mustSetPassword: false,
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'PASSWORD_SET',
          entityType: 'User',
          entityId: ctx.user.id,
          detailsJson: { timestamp: new Date().toISOString() },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { success: true, email: user.email }
    }),

  /**
   * Change password for current user (requires current password)
   */
  changePassword: protectedProcedure
    .input(
      z.object({
        currentPassword: z.string().min(1),
        newPassword: z.string().min(8),
        confirmNewPassword: z.string().min(8),
      })
    )
    .mutation(async ({ ctx, input }) => {
      // Get current user with password hash
      const user = await ctx.prisma.user.findUniqueOrThrow({
        where: { id: ctx.user.id },
        select: { passwordHash: true },
      })

      if (!user.passwordHash) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'No password set. Please use magic link to sign in.',
        })
      }

      // Verify current password
      const { verifyPassword } = await import('@/lib/password')
      const isValid = await verifyPassword(input.currentPassword, user.passwordHash)
      if (!isValid) {
        throw new TRPCError({
          code: 'UNAUTHORIZED',
          message: 'Current password is incorrect',
        })
      }

      // Validate new passwords match
      if (input.newPassword !== input.confirmNewPassword) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: 'New passwords do not match',
        })
      }

      // Validate new password requirements
      const validation = validatePassword(input.newPassword)
      if (!validation.valid) {
        throw new TRPCError({
          code: 'BAD_REQUEST',
          message: validation.errors.join('. '),
        })
      }

      // Hash the new password
      const passwordHash = await hashPassword(input.newPassword)

      // Update user with new password
      await ctx.prisma.user.update({
        where: { id: ctx.user.id },
        data: {
          passwordHash,
          passwordSetAt: new Date(),
        },
      })

      // Audit log
      await ctx.prisma.auditLog.create({
        data: {
          userId: ctx.user.id,
          action: 'PASSWORD_CHANGED',
          entityType: 'User',
          entityId: ctx.user.id,
          detailsJson: { timestamp: new Date().toISOString() },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { success: true }
    }),

  /**
   * Request password reset (public - no auth required)
   * Sends a magic link and marks user for password reset
   */
  requestPasswordReset: publicProcedure
    .input(z.object({ email: z.string().email() }))
    .mutation(async ({ ctx, input }) => {
      // Find user by email
      const user = await ctx.prisma.user.findUnique({
        where: { email: input.email },
        select: { id: true, email: true, status: true },
      })

      // Always return success to prevent email enumeration
      if (!user || user.status === 'SUSPENDED') {
        return { success: true, message: 'If an account exists with this email, a password reset link will be sent.' }
      }

      // Mark user for password reset
      await ctx.prisma.user.update({
        where: { id: user.id },
        data: { mustSetPassword: true },
      })

      // Generate a callback URL for the magic link
      const baseUrl = process.env.NEXTAUTH_URL || 'http://localhost:3000'
      const callbackUrl = `${baseUrl}/set-password`

      // We don't send the email here - the user will use the magic link form
      // This just marks them for password reset
      // The actual email is sent through NextAuth's email provider

      // Audit log (without user ID since this is public)
      await ctx.prisma.auditLog.create({
        data: {
          userId: null, // No authenticated user
          action: 'REQUEST_PASSWORD_RESET',
          entityType: 'User',
          entityId: user.id,
          detailsJson: { email: input.email, timestamp: new Date().toISOString() },
          ipAddress: ctx.ip,
          userAgent: ctx.userAgent,
        },
      })

      return { success: true, message: 'If an account exists with this email, a password reset link will be sent.' }
    }),
})
