import {
  auditLog, checkRateLimit, clearFailedLogins, createAuthToken, createSession, createUser,
  generateCsrfToken, getUserByEmail, isSetupMode, isUserLocked, recordLogin,
  registerFailedLogin, setPassword, updateUser, validateAuthToken, validatePasswordStrength, verifyPassword,
  validateSession, destroySession,
} from '../../core/identity/identity-store.mjs';
import { PlatformError } from '../../kernel/errors.mjs';

function fail(message, status = 400, code = 'AUTH_FAILED', details = null) {
  throw new PlatformError(message, { status, code, expose: true, details });
}

export function createAuthService({ setupTokenMatches }) {
  return Object.freeze({
    setupStatus({ mailConfigured, setupTokenRequired }) {
      return { setupMode: isSetupMode(), mailConfigured: Boolean(mailConfigured), setupTokenRequired: Boolean(setupTokenRequired) };
    },

    setup({ email, name, password, setupToken, ip, userAgent }) {
      if (!isSetupMode()) fail('Setup bereits abgeschlossen', 400, 'SETUP_COMPLETED');
      if (!setupTokenMatches(setupToken)) fail('Ungültiger Einrichtungs-Code.', 403, 'SETUP_TOKEN_INVALID');
      const passwordCheck = validatePasswordStrength(password);
      if (!passwordCheck.valid) fail(`Passwort benötigt ${passwordCheck.problems.join(', ')}.`, 400, 'PASSWORD_WEAK');
      try {
        const user = createUser(email, name || email.split('@')[0], 'admin', null, password);
        auditLog(user.id, email, 'user.created', 'user', String(user.id), 'Erster Admin (Setup)', ip);
        const session = createSession(user.id, ip, userAgent);
        recordLogin(user.id, ip, userAgent, true);
        auditLog(user.id, email, 'login.success', null, null, 'via setup', ip);
        return { user, session, csrf: generateCsrfToken() };
      } catch (error) {
        fail(error?.message || 'Setup fehlgeschlagen.', 500, 'SETUP_FAILED');
      }
    },

    login({ email, password, ip, userAgent }) {
      const rl = checkRateLimit(`login:${ip}`, 5, 15 * 60 * 1000);
      if (!rl.allowed) fail(`Zu viele Versuche. Bitte warte ${rl.retryAfter} Sekunden.`, 429, 'LOGIN_RATE_LIMIT', { retry_after: rl.retryAfter });
      const user = getUserByEmail(email);
      if (user && isUserLocked(user)) {
        auditLog(user.id, email, 'login.blocked_locked', null, null, user.locked_until, ip);
        fail('Account nach mehreren Fehlversuchen vorübergehend gesperrt.', 423, 'ACCOUNT_LOCKED', { locked_until: user.locked_until });
      }
      if (!user || !verifyPassword(password, user.password_hash)) {
        if (user) {
          recordLogin(user.id, ip, userAgent, false);
          const lockResult = registerFailedLogin(user.id);
          if (lockResult?.locked) {
            auditLog(user.id, email, 'login.account_locked', 'user', String(user.id), lockResult.lockedUntil, ip);
            fail('Zu viele Fehlversuche. Account für 15 Minuten gesperrt.', 423, 'ACCOUNT_LOCKED', { locked_until: lockResult.lockedUntil });
          }
        }
        auditLog(user?.id || null, email, 'login.failed', null, null, null, ip);
        fail('E-Mail oder Passwort falsch', 401, 'LOGIN_INVALID');
      }
      if (!user.active) fail('Account deaktiviert', 403, 'ACCOUNT_DISABLED');
      clearFailedLogins(user.id);
      const session = createSession(user.id, ip, userAgent);
      recordLogin(user.id, ip, userAgent, true);
      auditLog(user.id, email, 'login.success', null, null, 'via password', ip);
      return { user, session, csrf: generateCsrfToken() };
    },

    createInvite({ email, role, createdBy }) {
      return createAuthToken(email, 'invite', role || 'editor', createdBy);
    },

    acceptInvite({ token, password, name, ip, userAgent }) {
      const passwordCheck = validatePasswordStrength(password);
      if (!passwordCheck.valid) fail(`Passwort benötigt ${passwordCheck.problems.join(', ')}.`, 400, 'PASSWORD_WEAK');
      const authToken = validateAuthToken(token);
      if (!authToken) fail('Link ungültig oder abgelaufen', 400, 'INVITE_INVALID');
      let user = getUserByEmail(authToken.email);
      if (!user) {
        user = createUser(authToken.email, name || authToken.email.split('@')[0], authToken.role, authToken.created_by, password);
        auditLog(user.id, user.email, 'user.activated', 'user', String(user.id), null, ip);
      } else {
        setPassword(user.id, password);
        if (name) user = updateUser(user.id, { name });
      }
      if (!user.active) fail('Account deaktiviert', 403, 'ACCOUNT_DISABLED');
      const session = createSession(user.id, ip, userAgent);
      recordLogin(user.id, ip, userAgent, true);
      auditLog(user.id, user.email, 'login.success', null, null, 'via invite', ip);
      return { user, session, csrf: generateCsrfToken() };
    },

    logout({ token, ip }) {
      if (!token) return { ok: true };
      const session = validateSession(token);
      if (session) auditLog(session.user_id, session.email, 'logout', null, null, null, ip);
      destroySession(token);
      return { ok: true };
    },

    health() {
      return { ok: true, setup_mode: isSetupMode() };
    },
  });
}
