import { query, create, findOne, updateById, count } from '@zen/core/database';
import { hashPassword, verifyPassword, generateId } from './password.js';
import { createSession } from './session.js';
import { fail } from '@zen/core/shared/logger';
import { createEmailVerification, createPasswordReset, verifyResetToken, deleteResetToken, verifyAccountSetupToken, deleteAccountSetupToken } from './verifications.js';

async function register(userData, { onEmailVerification } = {}) {
  const { email, password, name } = userData;

  if (!email || !password || !name) {
    throw new Error('L\'e-mail, le mot de passe et le nom sont requis');
  }

  if (email.length > 254) {
    throw new Error('L\'e-mail doit contenir 254 caractères ou moins');
  }

  if (password.length < 8) {
    throw new Error('Le mot de passe doit contenir au moins 8 caractères');
  }

  if (password.length > 128) {
    throw new Error('Le mot de passe doit contenir 128 caractères ou moins');
  }

  const hasUppercase = /[A-Z]/.test(password);
  const hasLowercase = /[a-z]/.test(password);
  const hasNumber = /\d/.test(password);

  if (!hasUppercase || !hasLowercase || !hasNumber) {
    throw new Error('Le mot de passe doit contenir au moins une majuscule, une minuscule et un chiffre');
  }

  if (name.length > 100) {
    throw new Error('Le nom doit contenir 100 caractères ou moins');
  }

  if (name.trim().length === 0) {
    throw new Error('Le nom ne peut pas être vide');
  }

  const existingUser = await findOne('zen_auth_users', { email });
  if (existingUser) {
    throw new Error('Un utilisateur avec cet e-mail existe déjà');
  }

  const userCount = await count('zen_auth_users');
  const role = userCount === 0 ? 'admin' : 'user';

  const hashedPassword = await hashPassword(password);
  const userId = generateId();

  const user = await create('zen_auth_users', {
    id: userId,
    email,
    name,
    email_verified: false,
    image: null,
    role,
    updated_at: new Date()
  });

  const accountId = generateId();
  await create('zen_auth_accounts', {
    id: accountId,
    account_id: email,
    provider_id: 'credential',
    user_id: user.id,
    password: hashedPassword,
    updated_at: new Date()
  });

  // Assign admin role to first user via the new roles system
  if (role === 'admin') {
    try {
      const adminRole = await query(`SELECT id FROM zen_auth_roles WHERE name = 'admin'`);
      if (adminRole.rows.length > 0) {
        await query(
          `INSERT INTO zen_auth_user_roles (user_id, role_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
          [user.id, adminRole.rows[0].id]
        );
      }
    } catch (err) {
      fail(`register: failed to assign admin role to first user: ${err.message}`);
    }
  }

  const verification = await createEmailVerification(email);

  if (onEmailVerification) {
    try {
      await onEmailVerification(email, verification.token);
    } catch (err) {
      fail(`register: failed to send verification email: ${err.message}`);
    }
  }

  return { user, verificationToken: verification.token };
}

async function login(credentials, sessionOptions = {}) {
  const { email, password } = credentials;

  if (!email || !password) {
    throw new Error('L\'e-mail et le mot de passe sont requis');
  }

  const user = await findOne('zen_auth_users', { email });
  if (!user) {
    throw new Error('E-mail ou mot de passe incorrect');
  }

  const account = await findOne('zen_auth_accounts', {
    user_id: user.id,
    provider_id: 'credential'
  });

  if (!account || !account.password) {
    throw new Error('E-mail ou mot de passe incorrect');
  }

  const isValid = await verifyPassword(password, account.password);
  if (!isValid) {
    throw new Error('E-mail ou mot de passe incorrect');
  }

  const session = await createSession(user.id, sessionOptions);

  return { user, session };
}

async function requestPasswordReset(email) {
  if (!email) {
    throw new Error('L\'e-mail est requis');
  }

  const user = await findOne('zen_auth_users', { email });
  if (!user) {
    return { success: true };
  }

  const reset = await createPasswordReset(email);

  return { success: true, token: reset.token };
}

async function resetPassword(resetData, { onPasswordChanged } = {}) {
  const { email, token, newPassword } = resetData;

  if (!email || !token || !newPassword) {
    throw new Error('L\'e-mail, le jeton et le nouveau mot de passe sont requis');
  }

  if (newPassword.length < 8) {
    throw new Error('Le mot de passe doit contenir au moins 8 caractères');
  }

  if (newPassword.length > 128) {
    throw new Error('Le mot de passe doit contenir 128 caractères ou moins');
  }

  const hasUppercase = /[A-Z]/.test(newPassword);
  const hasLowercase = /[a-z]/.test(newPassword);
  const hasNumber = /\d/.test(newPassword);

  if (!hasUppercase || !hasLowercase || !hasNumber) {
    throw new Error('Le mot de passe doit contenir au moins une majuscule, une minuscule et un chiffre');
  }

  const tokenValid = await verifyResetToken(email, token);
  if (!tokenValid) {
    throw new Error('Jeton de réinitialisation invalide ou expiré');
  }

  const user = await findOne('zen_auth_users', { email });
  if (!user) {
    throw new Error('Jeton de réinitialisation invalide');
  }

  const account = await findOne('zen_auth_accounts', {
    user_id: user.id,
    provider_id: 'credential'
  });

  if (!account) {
    throw new Error('Compte introuvable');
  }

  const hashedPassword = await hashPassword(newPassword);

  await updateById('zen_auth_accounts', account.id, {
    password: hashedPassword,
    updated_at: new Date()
  });

  await deleteResetToken(email);

  if (onPasswordChanged) {
    try {
      await onPasswordChanged(email);
    } catch (error) {
      fail(`Auth: failed to send password changed email to ${email}: ${error.message}`);
    }
  }

  return { success: true };
}

async function verifyUserEmail(userId) {
  return await updateById('zen_auth_users', userId, {
    email_verified: true,
    updated_at: new Date()
  });
}

async function updateUser(userId, updateData) {
  const allowedFields = ['name', 'image', 'language'];
  const filteredData = {};

  for (const field of allowedFields) {
    if (updateData[field] !== undefined) {
      filteredData[field] = updateData[field];
    }
  }

  filteredData.updated_at = new Date();

  return await updateById('zen_auth_users', userId, filteredData);
}

async function completeAccountSetup({ email, token, password }) {
  if (!email || !token || !password) {
    throw new Error('L\'e-mail, le jeton et le mot de passe sont requis');
  }

  if (password.length < 8) {
    throw new Error('Le mot de passe doit contenir au moins 8 caractères');
  }

  if (password.length > 128) {
    throw new Error('Le mot de passe doit contenir 128 caractères ou moins');
  }

  const hasUppercase = /[A-Z]/.test(password);
  const hasLowercase = /[a-z]/.test(password);
  const hasNumber = /\d/.test(password);

  if (!hasUppercase || !hasLowercase || !hasNumber) {
    throw new Error('Le mot de passe doit contenir au moins une majuscule, une minuscule et un chiffre');
  }

  const tokenValid = await verifyAccountSetupToken(email, token);
  if (!tokenValid) {
    throw new Error('Lien d\'invitation invalide ou expiré');
  }

  const user = await findOne('zen_auth_users', { email });
  if (!user) {
    throw new Error('Lien d\'invitation invalide');
  }

  const hashedPassword = await hashPassword(password);

  const existingAccount = await findOne('zen_auth_accounts', {
    user_id: user.id,
    provider_id: 'credential'
  });

  if (existingAccount) {
    await updateById('zen_auth_accounts', existingAccount.id, {
      password: hashedPassword,
      updated_at: new Date()
    });
  } else {
    await create('zen_auth_accounts', {
      id: generateId(),
      account_id: email,
      provider_id: 'credential',
      user_id: user.id,
      password: hashedPassword,
      updated_at: new Date()
    });
  }

  await updateById('zen_auth_users', user.id, {
    email_verified: true,
    updated_at: new Date()
  });

  await deleteAccountSetupToken(email);

  return { success: true };
}

export { register, login, requestPasswordReset, resetPassword, verifyUserEmail, updateUser, completeAccountSetup };
