import jwt from 'jsonwebtoken';
import { hash, compare } from 'bcryptjs';
import type { AstroCookies } from 'astro';

const JWT_SECRET = import.meta.env.JWT_SECRET || 'dev-secret-change-in-production';
const JWT_EXPIRES_IN = import.meta.env.JWT_EXPIRES_IN || '7d';

export interface JWTPayload {
  userId: string;
  email: string;
  role: 'customer' | 'vendor' | 'admin';
  iat?: number;
  exp?: number;
}

export interface AuthUser {
  id: string;
  email: string;
  name: string | null;
  role: 'customer' | 'vendor' | 'admin';
}

// Password hashing
export async function hashPassword(password: string): Promise<string> {
  return hash(password, 12);
}

export async function verifyPassword(password: string, hashedPassword: string): Promise<boolean> {
  return compare(password, hashedPassword);
}

// JWT Token management
export function generateToken(payload: Omit<JWTPayload, 'iat' | 'exp'>): string {
  return jwt.sign(payload, JWT_SECRET, { expiresIn: JWT_EXPIRES_IN });
}

export function verifyToken(token: string): JWTPayload | null {
  try {
    return jwt.verify(token, JWT_SECRET) as JWTPayload;
  } catch {
    return null;
  }
}

export function decodeToken(token: string): JWTPayload | null {
  try {
    return jwt.decode(token) as JWTPayload;
  } catch {
    return null;
  }
}

// Extract token from Authorization header
export function getTokenFromHeader(authHeader: string | null): string | null {
  if (!authHeader?.startsWith('Bearer ')) return null;
  return authHeader.slice(7);
}

// Extract token from cookie
export function getTokenFromCookie(cookies: AstroCookies): string | null {
  const token = cookies.get('auth_token')?.value;
  return token || null;
}

// Get current user from request
export function getCurrentUser(request: Request, cookies: AstroCookies): AuthUser | null {
  // Try header first
  const authHeader = request.headers.get('authorization');
  let token = getTokenFromHeader(authHeader);
  
  // Fallback to cookie
  if (!token) {
    token = getTokenFromCookie(cookies);
  }
  
  if (!token) return null;
  
  const payload = verifyToken(token);
  if (!payload) return null;
  
  return {
    id: payload.userId,
    email: payload.email,
    name: null,
    role: payload.role,
  };
}

// Cookie helpers
export const AUTH_COOKIE_OPTIONS = {
  httpOnly: true,
  secure: import.meta.env.PROD,
  sameSite: 'lax' as const,
  path: '/',
  maxAge: 60 * 60 * 24 * 7, // 7 days
};

export function setAuthCookie(cookies: AstroCookies, token: string): void {
  cookies.set('auth_token', token, AUTH_COOKIE_OPTIONS);
}

export function clearAuthCookie(cookies: AstroCookies): void {
  cookies.delete('auth_token', { path: '/' });
}

// Role-based access control
export function isAdmin(user: AuthUser | null): boolean {
  return user?.role === 'admin';
}

export function isVendor(user: AuthUser | null): boolean {
  return user?.role === 'vendor' || user?.role === 'admin';
}

export function isCustomer(user: AuthUser | null): boolean {
  return user?.role === 'customer';
}

// Require authentication middleware helper
export function requireAuth(user: AuthUser | null): AuthUser {
  if (!user) {
    throw new Error('Unauthorized');
  }
  return user;
}

// Require role middleware helper
export function requireRole(user: AuthUser | null, ...roles: AuthUser['role'][]): AuthUser {
  const authenticatedUser = requireAuth(user);
  if (!roles.includes(authenticatedUser.role)) {
    throw new Error('Forbidden');
  }
  return authenticatedUser;
}

// Generate reset token (for password reset flow)
export function generateResetToken(userId: string, email: string): string {
  return jwt.sign(
    { userId, email, type: 'reset' },
    JWT_SECRET,
    { expiresIn: '1h' }
  );
}

export function verifyResetToken(token: string): { userId: string; email: string } | null {
  try {
    const decoded = jwt.verify(token, JWT_SECRET) as { userId: string; email: string; type: string };
    if (decoded.type !== 'reset') return null;
    return { userId: decoded.userId, email: decoded.email };
  } catch {
    return null;
  }
}

// Generate email verification token
export function generateVerificationToken(userId: string, email: string): string {
  return jwt.sign(
    { userId, email, type: 'verification' },
    JWT_SECRET,
    { expiresIn: '24h' }
  );
}

export function verifyVerificationToken(token: string): { userId: string; email: string } | null {
  try {
    const decoded = jwt.verify(token, JWT_SECRET) as { userId: string; email: string; type: string };
    if (decoded.type !== 'verification') return null;
    return { userId: decoded.userId, email: decoded.email };
  } catch {
    return null;
  }
}
