import type { Request, Response } from 'express';
import prisma from '../config/db';
import {
  cancelPasswordRecoveryRequestsForUser,
  completePasswordRecoveryRequest,
  createPasswordRecoveryRequest,
  findPasswordRecoveryRequest,
} from '../services/password-recovery.store';
import {
  findFallbackUserByEmail,
  sanitizeFallbackUser,
  updateFallbackUserPassword,
} from '../services/settings.store';
import { resolveOperationalUser } from '../services/system.resolvers';

type DatabaseUserRecord = {
  id: string;
  email: string;
  name: string;
  role: string;
  active: boolean;
  password: string;
  createdAt: Date;
  updatedAt: Date;
};

const sanitizeDatabaseUser = (user: DatabaseUserRecord) => ({
  id: user.id,
  email: user.email,
  name: user.name,
  role: user.role,
  active: user.active,
  createdAt: user.createdAt.toISOString(),
  updatedAt: user.updatedAt.toISOString(),
});

const normalizeEmail = (value: unknown) => (typeof value === 'string' ? value.trim().toLowerCase() : '');
const normalizePassword = (value: unknown) => (typeof value === 'string' ? value.trim() : '');

const findUserForRecovery = async (normalizedEmail: string) => {
  try {
    const user = await prisma.user.findUnique({
      where: { email: normalizedEmail },
    });

    if (user && user.active) {
      return {
        user,
        sourceMode: 'database' as const,
      };
    }
  } catch (error) {
    console.warn('Database unavailable for password recovery lookup, using local fallback store.', error);
  }

  const fallbackUser = await findFallbackUserByEmail(normalizedEmail);

  if (fallbackUser && fallbackUser.active) {
    return {
      user: fallbackUser,
      sourceMode: 'local-fallback' as const,
    };
  }

  return null;
};

export const login = async (req: Request, res: Response) => {
  const email = normalizeEmail(req.body.email);
  const password = normalizePassword(req.body.password);

  if (!email || !password) {
    return res.status(400).json({ error: 'Correo y contrasena son obligatorios' });
  }

  try {
    await resolveOperationalUser();

    const user = await prisma.user.findUnique({
      where: { email },
    });

    if (!user || !user.active || user.password !== password) {
      return res.status(401).json({ error: 'Credenciales invalidas' });
    }

    res.setHeader('X-Data-Source', 'database');
    return res.json({
      user: sanitizeDatabaseUser(user),
    });
  } catch (error) {
    console.warn('Database unavailable for auth, using local fallback store.', error);

    const user = await findFallbackUserByEmail(email);

    if (!user || !user.active || user.password !== password) {
      return res.status(401).json({ error: 'Credenciales invalidas' });
    }

    res.setHeader('X-Data-Source', 'local-fallback');
    return res.json({
      user: sanitizeFallbackUser(user),
    });
  }
};

export const requestPasswordRecovery = async (req: Request, res: Response) => {
  const email = normalizeEmail(req.body.email);

  if (!email) {
    return res.status(400).json({ error: 'Debes ingresar un correo valido' });
  }

  const resolvedUser = await findUserForRecovery(email);

  if (resolvedUser) {
    await createPasswordRecoveryRequest({
      userId: resolvedUser.user.id,
      email: resolvedUser.user.email,
      userName: resolvedUser.user.name,
      sourceMode: resolvedUser.sourceMode,
    });
  }

  return res.json({
    message:
      'Si el correo existe, la solicitud fue registrada. Pide al administrador el codigo temporal desde Configuracion.',
  });
};

export const completePasswordRecovery = async (req: Request, res: Response) => {
  const email = normalizeEmail(req.body.email);
  const code = typeof req.body.code === 'string' ? req.body.code.trim() : '';
  const nextPassword = normalizePassword(req.body.newPassword);

  if (!email || !code || !nextPassword) {
    return res.status(400).json({ error: 'Correo, codigo y nueva contrasena son obligatorios' });
  }

  if (nextPassword.length < 6) {
    return res.status(400).json({ error: 'La nueva contrasena debe tener al menos 6 caracteres' });
  }

  const recoveryRequest = await findPasswordRecoveryRequest(email, code);

  if (!recoveryRequest) {
    return res.status(400).json({ error: 'El codigo es invalido o ya vencio' });
  }

  try {
    if (recoveryRequest.sourceMode === 'database') {
      await prisma.user.update({
        where: { id: recoveryRequest.userId },
        data: { password: nextPassword, active: true },
      });
    } else {
      const user = await updateFallbackUserPassword(recoveryRequest.userId, nextPassword);

      if (!user) {
        return res.status(404).json({ error: 'Usuario no encontrado para restablecer la contrasena' });
      }
    }

    await completePasswordRecoveryRequest(recoveryRequest.id);
    await cancelPasswordRecoveryRequestsForUser(recoveryRequest.userId);

    return res.json({
      message: 'Contrasena actualizada correctamente. Ya puedes iniciar sesion.',
    });
  } catch (error) {
    console.error('Error completing password recovery:', error);
    return res.status(500).json({ error: 'Error interno del servidor' });
  }
};
