/**
 * Services de gestion des utilisateurs
 * Création, récupération et gestion des comptes utilisateurs
 */
import type { Pool } from "mysql2/promise";
import { randomId } from "./crypto-utils.js";
import type { UserRecord, UserRow, User } from "./types.js";

/**
 * Mappe une ligne SQL vers un UserRecord
 */
export const mapUserRow = (row: UserRow): UserRecord => ({
  id: row.id,
  email: row.email,
  displayName: row.displayName || row.email.split("@")[0] || "Joueur",
  emailVerified: Boolean(row.emailVerified ?? true),
  avatar: row.avatar ?? null,
  accent: row.accent ?? null,
  title: row.title ?? null,
  motto: row.motto ?? null,
  guest: Boolean(row.guest),
  isAdmin: Boolean(row.isAdmin),
  vipNoAds: Boolean(row.vipNoAds),
  vipExpiresAt: row.vipExpiresAt ?? null,
  passwordHash: row.passwordHash ?? null,
  failedAttempts: Number(row.failedAttempts ?? 0) || 0,
  lockedUntil: row.lockedUntil ?? null,
  resetTokenHash: row.resetTokenHash ?? null,
  resetTokenExpires: row.resetTokenExpires ?? null,
  emailVerifiedAt: row.emailVerifiedAt ?? null,
  emailVerifyTokenHash: row.emailVerifyTokenHash ?? null,
  emailVerifyTokenExpires: row.emailVerifyTokenExpires ?? null,
  bannedAt: row.bannedAt ?? null,
  banUntil: row.banUntil ?? null,
  banReason: row.banReason ?? null,
});

/**
 * Convertit un UserRecord en User public (sans données sensibles)
 */
export const toPublicUser = (user: UserRecord): User => ({
  id: user.id,
  email: user.email,
  displayName: user.displayName,
  emailVerified: user.emailVerified,
  avatar: user.avatar,
  accent: user.accent,
  title: user.title,
  motto: user.motto,
  guest: user.guest,
  isAdmin: user.isAdmin,
  vipNoAds: user.vipNoAds,
  vipExpiresAt: user.vipExpiresAt,
});

/**
 * Récupère un utilisateur par email
 */
export const getUserByEmail = async (
  db: Pool,
  email: string,
): Promise<UserRecord | null> => {
  const [rows] = await db.execute(
    "SELECT id, email, password_hash as passwordHash, failed_login_attempts as failedAttempts, locked_until as lockedUntil, reset_token_hash as resetTokenHash, reset_token_expires as resetTokenExpires, email_verified as emailVerified, email_verified_at as emailVerifiedAt, email_verify_token_hash as emailVerifyTokenHash, email_verify_token_expires as emailVerifyTokenExpires, display_name as displayName, avatar, accent, title, motto, guest, is_admin as isAdmin, vip_no_ads as vipNoAds, vip_expires_at as vipExpiresAt, banned_at as bannedAt, ban_until as banUntil, ban_reason as banReason FROM users WHERE email = ? LIMIT 1",
    [email],
  );
  if (Array.isArray(rows) && rows[0]) {
    return mapUserRow(rows[0] as UserRow);
  }
  return null;
};

/**
 * Récupère un utilisateur par ID
 */
export const getUserById = async (
  db: Pool,
  userId: number,
): Promise<UserRecord | null> => {
  const [rows] = await db.execute(
    "SELECT id, email, password_hash as passwordHash, failed_login_attempts as failedAttempts, locked_until as lockedUntil, reset_token_hash as resetTokenHash, reset_token_expires as resetTokenExpires, email_verified as emailVerified, email_verified_at as emailVerifiedAt, email_verify_token_hash as emailVerifyTokenHash, email_verify_token_expires as emailVerifyTokenExpires, display_name as displayName, avatar, accent, title, motto, guest, is_admin as isAdmin, vip_no_ads as vipNoAds, vip_expires_at as vipExpiresAt, banned_at as bannedAt, ban_until as banUntil, ban_reason as banReason FROM users WHERE id = ? LIMIT 1",
    [userId],
  );
  if (Array.isArray(rows) && rows[0]) {
    return mapUserRow(rows[0] as UserRow);
  }
  return null;
};

/**
 * Crée un nouvel utilisateur
 */
export const createUser = async (
  db: Pool,
  payload: { email: string; displayName: string; passwordHash: string },
): Promise<UserRecord> => {
  const [result] = await db.execute(
    "INSERT INTO users (email, password_hash, display_name, guest, is_admin, vip_no_ads, failed_login_attempts, email_verified, email_verified_at, email_verify_token_hash, email_verify_token_expires) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
    [
      payload.email,
      payload.passwordHash,
      payload.displayName,
      false,
      false,
      false,
      0,
      false,
      null,
      null,
      null,
    ],
  );
  const insertId = (result as { insertId?: number }).insertId ?? 0;
  return {
    id: insertId,
    email: payload.email,
    displayName: payload.displayName,
    emailVerified: false,
    avatar: null,
    accent: null,
    title: null,
    motto: null,
    guest: false,
    isAdmin: false,
    vipNoAds: false,
    vipExpiresAt: null,
    passwordHash: payload.passwordHash,
    failedAttempts: 0,
    lockedUntil: null,
    resetTokenHash: null,
    resetTokenExpires: null,
    emailVerifiedAt: null,
    emailVerifyTokenHash: null,
    emailVerifyTokenExpires: null,
    bannedAt: null,
    banUntil: null,
    banReason: null,
  };
};

/**
 * Crée un utilisateur invité
 */
export const createGuestUser = async (db: Pool): Promise<UserRecord> => {
  const rawId = randomId();
  const email = `invite-${rawId}@rollerlogic.local`;
  const displayName = `Invite-${rawId.toUpperCase()}`;
  const [result] = await db.execute(
    "INSERT INTO users (email, display_name, guest, is_admin, failed_login_attempts) VALUES (?, ?, ?, ?, ?)",
    [email, displayName, true, false, 0],
  );
  const insertId = (result as { insertId?: number }).insertId ?? 0;
  return {
    id: insertId,
    email,
    displayName,
    emailVerified: true,
    avatar: null,
    accent: null,
    title: null,
    motto: null,
    guest: true,
    isAdmin: false,
    vipNoAds: false,
    vipExpiresAt: null,
    passwordHash: null,
    failedAttempts: 0,
    lockedUntil: null,
    resetTokenHash: null,
    resetTokenExpires: null,
    emailVerifiedAt: new Date(),
    emailVerifyTokenHash: null,
    emailVerifyTokenExpires: null,
    bannedAt: null,
    banUntil: null,
    banReason: null,
  };
};

/**
 * Vérifie si un utilisateur est admin
 */
export const isAdminUser = async (
  db: Pool,
  userId: number,
): Promise<boolean> => {
  const [rows] = await db.execute(
    "SELECT is_admin FROM users WHERE id = ? LIMIT 1",
    [userId],
  );
  if (Array.isArray(rows) && rows[0]) {
    return Boolean((rows[0] as { is_admin?: number | boolean }).is_admin);
  }
  return false;
};
