import crypto from 'crypto';
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import { UserCreatedVia, UserRole, UserStatus } from '@prisma/client';
import { prisma } from '../../config/database';
import { env } from '../../config/env';
import { authService } from '../auth/auth.service';
import { AppError } from '../../middleware/error-handler';
import type { RedeemInviteInput } from './volunteer-invite.schemas';

interface InviteTokenPayload {
  type: 'volunteer_invite';
  adminUserId: string;
  cutId?: string;
  shiftId?: string;
}

export const volunteerInviteService = {
  /**
   * Generate a signed invite token (JWT, 30 min expiry).
   * Contains the inviting admin's ID and optional cut/shift context.
   */
  generateInviteToken(adminUserId: string, cutId?: string, shiftId?: string): string {
    const payload: InviteTokenPayload = {
      type: 'volunteer_invite',
      adminUserId,
      ...(cutId && { cutId }),
      ...(shiftId && { shiftId }),
    };

    return jwt.sign(payload, env.JWT_INVITE_SECRET, { algorithm: 'HS256', expiresIn: '30m' });
  },

  /**
   * Redeem an invite token: verify it, create (or reactivate) a TEMP user,
   * and return a JWT token pair for immediate login.
   */
  async redeemInvite(input: RedeemInviteInput) {
    // 1. Verify and decode the invite token
    let payload: InviteTokenPayload;
    try {
      const decoded = jwt.verify(input.token, env.JWT_INVITE_SECRET, { algorithms: ['HS256'] });
      payload = decoded as InviteTokenPayload;
    } catch {
      throw new AppError(400, 'Invalid or expired invite link', 'INVALID_INVITE_TOKEN');
    }

    // 2. Validate token type to prevent JWT confusion attacks
    if (payload.type !== 'volunteer_invite') {
      throw new AppError(400, 'Invalid invite token', 'INVALID_TOKEN_TYPE');
    }

    const email = input.email.toLowerCase().trim();
    const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // +24h

    // 3. Check for existing user
    const existingUser = await prisma.user.findUnique({ where: { email } });

    if (existingUser) {
      // Active non-TEMP user — just generate new token pair (re-login)
      if (existingUser.status === UserStatus.ACTIVE && existingUser.role !== UserRole.TEMP) {
        const tokens = await authService.generateTokenPair(existingUser);
        return { tokens, user: existingUser, cutId: payload.cutId, shiftId: payload.shiftId };
      }

      // Expired or inactive TEMP user — reactivate with extended expiry
      if (existingUser.role === UserRole.TEMP) {
        const reactivated = await prisma.user.update({
          where: { id: existingUser.id },
          data: {
            status: UserStatus.ACTIVE,
            expiresAt,
            name: input.name || existingUser.name,
            phone: input.phone || existingUser.phone,
          },
        });
        const tokens = await authService.generateTokenPair(reactivated);
        return { tokens, user: reactivated, cutId: payload.cutId, shiftId: payload.shiftId };
      }

      // Suspended/inactive non-TEMP user — block
      throw new AppError(403, 'Account is not active. Please contact an administrator.', 'ACCOUNT_INACTIVE');
    }

    // 4. Create new TEMP user with random password (never shown to user)
    const randomPassword = crypto.randomBytes(16).toString('hex');
    const hashedPassword = await bcrypt.hash(randomPassword, 12);

    const newUser = await prisma.user.create({
      data: {
        email,
        password: hashedPassword,
        name: input.name || null,
        phone: input.phone || null,
        role: UserRole.TEMP,
        roles: JSON.stringify([UserRole.TEMP]),
        status: UserStatus.ACTIVE,
        createdVia: UserCreatedVia.QUICK_JOIN_INVITE,
        expiresAt,
      },
    });

    const tokens = await authService.generateTokenPair(newUser);
    return { tokens, user: newUser, cutId: payload.cutId, shiftId: payload.shiftId };
  },
};
