import bcrypt from 'bcrypt';
import jwt, { SignOptions } from 'jsonwebtoken';

if (!process.env.JWT_SECRET && process.env.NODE_ENV === 'production') {
    throw new Error('FATAL: JWT_SECRET is not defined in production environment');
}

const JWT_SECRET = process.env.JWT_SECRET || 'dev-secret-do-not-use-in-prod';
const SALT_ROUNDS = 10;

// Unique ID for this server instance. Used to invalidate sessions on restart if needed.
export const SERVER_BOOT_ID = Math.random().toString(36).substring(2, 10);

export interface TokenPayload {
    userId: number;
    email: string;
    role: 'user' | 'admin';
    bootId?: string;
}

/**
 * Hash a password using bcrypt
 */
export async function hashPassword(password: string): Promise<string> {
    return bcrypt.hash(password, SALT_ROUNDS);
}

/**
 * Compare a plain password with a hashed password
 */
export async function comparePassword(
    password: string,
    hash: string
): Promise<boolean> {
    return bcrypt.compare(password, hash);
}

/**
 * Generate a JWT token
 */
export function generateToken(payload: TokenPayload, expiresIn: string = '2h'): string {
    const options: SignOptions = {
        expiresIn: expiresIn as SignOptions['expiresIn']
    };
    // Include the current boot ID in every token
    const tokenPayload = { ...payload, bootId: SERVER_BOOT_ID };
    return jwt.sign(tokenPayload, JWT_SECRET, options);
}

/**
 * Verify and decode a JWT token
 */
export function verifyToken(token: string): TokenPayload {
    try {
        const decoded = jwt.verify(token, JWT_SECRET) as TokenPayload;

        // Security: If the token was issued by a previous server instance, invalidate it
        if (decoded.bootId !== SERVER_BOOT_ID) {
            throw new Error('Session invalidated due to server restart');
        }

        return decoded;
    } catch (error: unknown) {
        if (error instanceof Error) {
            throw new Error(error.message);
        }
        throw new Error('Invalid or expired token');
    }
}
