<?php
/**
 * ASISMA CRM - Controlador de Autenticación
 */
class AuthController extends Controller {

    public function login() {
        if (SessionHelper::checkSession()) {
            $this->redirect('dashboard');
        }

        $this->renderPartial('auth/login', array(
            'title' => 'Iniciar Sesión - ASISMA CRM'
        ));
    }

    public function authenticate() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->json(array('success' => false, 'message' => 'Método no soportado.'), 405);
        }

        $email    = $this->input('email');
        $password = isset($_POST['password']) ? $_POST['password'] : '';

        if (empty($email) || empty($password)) {
            $this->json(array('success' => false, 'message' => 'El correo y la contraseña son obligatorios.'));
        }

        $db = Database::getInstance();

        $sql  = "SELECT u.*, ef.razon_social as empresa_nombre,
                        f.vertical_id as franquiciado_vertical_id
                 FROM usuarios u
                 INNER JOIN empresas_franquiciados ef ON u.empresa_id = ef.id
                 LEFT JOIN franquiciados f ON u.franquiciado_id = f.id
                 WHERE u.email = :email LIMIT 1";
        $user = $db->fetchOne($sql, array('email' => $email));

        if (!$user) {
            $this->json(array('success' => false, 'message' => 'Las credenciales ingresadas son incorrectas.'));
        }

        if (!$user['activo']) {
            $this->json(array('success' => false, 'message' => 'Su cuenta se encuentra inactiva. Contacte al administrador.'));
        }

        if (!password_verify($password, $user['password_hash'])) {
            $this->json(array('success' => false, 'message' => 'Las credenciales ingresadas son incorrectas.'));
        }

        $db->update('usuarios', array('ultimo_login' => date('Y-m-d H:i:s')), "id = :id", array('id' => $user['id']));

        SessionHelper::startSession($user);

        // "Recordar dispositivo": deja una cookie segura de 30 días para no
        // pedir login de nuevo en este equipo hasta que venza o cierre sesión.
        if ($this->input('remember')) {
            SessionHelper::crearTokenRecordado($user['id']);
        }

        AuditHelper::log('login_success', 'usuarios', $user['id'], null, array(
            'email' => $user['email'],
            'rol'   => $user['rol']
        ));

        $returnUrl = $this->input('return_url', 'dashboard');

        $this->json(array(
            'success'  => true,
            'message'  => 'Autenticación exitosa. Redireccionando...',
            'redirect' => APP_URL . '/' . ltrim($returnUrl, '/')
        ));
    }

    public function logout() {
        if (SessionHelper::getUsuarioId()) {
            AuditHelper::log('logout', 'usuarios', SessionHelper::getUsuarioId());
        }

        SessionHelper::destroySession();
        $this->redirect('login');
    }

    // =====================================================================
    // RECUPERACION DE CONTRASENA
    // =====================================================================

    /** Formulario "Olvide mi contrasena": pide el correo. */
    public function olvidePassword() {
        if (SessionHelper::checkSession()) {
            $this->redirect('dashboard');
        }
        $this->renderPartial('auth/olvide', array(
            'title' => 'Recuperar Contraseña - ASISMA CRM'
        ));
    }

    /**
     * POST: recibe el correo, genera el enlace de recuperacion y lo envia.
     * Por seguridad SIEMPRE responde igual (no revela si el correo existe).
     */
    public function enviarReset() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->json(array('success' => false, 'message' => 'Método no soportado.'), 405);
        }

        $email = trim($this->input('email', ''));
        // Respuesta neutra: la misma exista o no el correo (evita enumerar usuarios)
        $respuestaNeutra = array(
            'success' => true,
            'message' => 'Si el correo está registrado, te enviamos un enlace para restablecer tu contraseña. Revisa tu bandeja de entrada (y el spam).'
        );

        if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            $this->json(array('success' => false, 'message' => 'Ingresa un correo electrónico válido.'));
        }

        $db   = Database::getInstance();
        $user = $db->fetchOne(
            "SELECT id, nombre, email, activo FROM usuarios WHERE email = :email LIMIT 1",
            array('email' => $email)
        );

        // Solo se envia a usuarios existentes y activos; en cualquier otro caso
        // se responde igual para no filtrar informacion.
        if (!$user || !$user['activo']) {
            AuditHelper::log('password_reset_solicitado', 'usuarios', $user ? $user['id'] : null, null,
                array('email' => $email, 'enviado' => false, 'motivo' => $user ? 'inactivo' : 'no_existe'));
            $this->json($respuestaNeutra);
        }

        // Token en claro (viaja en el enlace) + su hash (se guarda en BD)
        $token     = bin2hex(openssl_random_pseudo_bytes(32));
        $tokenHash = hash('sha256', $token);

        // Invalidar los tokens vigentes anteriores de este usuario
        $db->query(
            "UPDATE password_resets SET usado_at = NOW()
             WHERE usuario_id = :uid AND usado_at IS NULL",
            array('uid' => (int)$user['id'])
        );

        // El vencimiento se calcula con el reloj de MySQL (DATE_ADD(NOW(), ...))
        // igual que la comparacion en tokenValido: asi el timezone de PHP no
        // afecta la vigencia del token (PHP y MySQL pueden ir en horas distintas).
        $db->query(
            "INSERT INTO password_resets (usuario_id, email, token_hash, expira_at, ip)
             VALUES (:uid, :email, :hash, DATE_ADD(NOW(), INTERVAL 1 HOUR), :ip)",
            array(
                'uid'   => (int)$user['id'],
                'email' => $user['email'],
                'hash'  => $tokenHash,
                'ip'    => $this->ipCliente()
            )
        );

        $enlace = APP_URL . '/password/restablecer?token=' . $token;
        $nombre = htmlspecialchars($user['nombre']);
        $cuerpo = '<p>Hola <b>' . $nombre . '</b>,</p>'
            . '<p>Recibimos una solicitud para restablecer la contraseña de tu cuenta en ASISMA CRM. '
            . 'Haz clic en el botón para crear una nueva contraseña:</p>'
            . '<p style="margin:22px 0;">'
            . '<a href="' . $enlace . '" style="background:#2563eb;color:#fff;padding:12px 24px;border-radius:8px;text-decoration:none;font-weight:bold;display:inline-block;">Restablecer mi contraseña</a>'
            . '</p>'
            . '<p style="font-size:12px;color:#666;">O copia y pega este enlace en tu navegador:<br>'
            . '<a href="' . $enlace . '">' . $enlace . '</a></p>'
            . '<p style="font-size:12px;color:#666;">Este enlace vence en <b>1 hora</b> y solo puede usarse una vez. '
            . 'Si tú no pediste este cambio, ignora este correo: tu contraseña seguirá siendo la misma.</p>';

        $envio = CorreoHelper::enviar($user['email'], 'Restablece tu contraseña - ASISMA CRM', $cuerpo);

        AuditHelper::log('password_reset_solicitado', 'usuarios', (int)$user['id'], null,
            array('email' => $user['email'], 'enviado' => $envio['success'], 'detalle' => $envio['detalle']));

        // Aunque el correo falle, la respuesta al usuario es la misma (neutra).
        if (!$envio['success']) {
            error_log('[Password reset] No se pudo enviar el correo a ' . $user['email'] . ': ' . $envio['detalle']);
        }
        $this->json($respuestaNeutra);
    }

    /** Formulario para escribir la nueva contrasena (valida el token del enlace). */
    public function restablecerPassword() {
        if (SessionHelper::checkSession()) {
            $this->redirect('dashboard');
        }
        $token = $this->input('token', '');
        $fila  = $this->tokenValido($token);

        $this->renderPartial('auth/restablecer', array(
            'title'   => 'Nueva Contraseña - ASISMA CRM',
            'token'   => $token,
            'valido'  => (bool)$fila
        ));
    }

    /** POST: valida el token y guarda la nueva contrasena. */
    public function guardarPassword() {
        if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
            $this->json(array('success' => false, 'message' => 'Método no soportado.'), 405);
        }

        $token    = $this->input('token', '');
        $password = isset($_POST['password']) ? $_POST['password'] : '';
        $confirma = isset($_POST['password_confirmacion']) ? $_POST['password_confirmacion'] : '';

        $fila = $this->tokenValido($token);
        if (!$fila) {
            $this->json(array('success' => false, 'message' => 'El enlace no es válido o ya venció. Solicita uno nuevo desde "¿Olvidaste tu contraseña?".'));
        }

        $errorClave = ClaveHelper::validar($password);
        if ($errorClave !== null) {
            $this->json(array('success' => false, 'message' => $errorClave));
        }
        if ($password !== $confirma) {
            $this->json(array('success' => false, 'message' => 'Las contraseñas no coinciden.'));
        }

        $db = Database::getInstance();
        $db->update('usuarios',
            array('password_hash' => password_hash($password, PASSWORD_BCRYPT)),
            "id = :id", array('id' => (int)$fila['usuario_id']));

        // El token queda usado (un solo uso) — reloj de MySQL
        $db->query("UPDATE password_resets SET usado_at = NOW() WHERE id = :id",
            array('id' => (int)$fila['id']));

        AuditHelper::log('password_reset_completado', 'usuarios', (int)$fila['usuario_id'], null,
            array('email' => $fila['email']));

        $this->json(array(
            'success'  => true,
            'message'  => 'Tu contraseña fue actualizada. Ya puedes iniciar sesión.',
            'redirect' => APP_URL . '/login'
        ));
    }

    /**
     * Devuelve la fila del token si es valido (existe, no usado y no vencido),
     * o null. Compara por hash del token en tiempo constante.
     */
    private function tokenValido($token) {
        if (!is_string($token) || strlen($token) !== 64 || !ctype_xdigit($token)) {
            return null;
        }
        $db  = Database::getInstance();
        $tokenHash = hash('sha256', $token);
        $fila = $db->fetchOne(
            "SELECT id, usuario_id, email, token_hash FROM password_resets
             WHERE token_hash = :h AND usado_at IS NULL AND expira_at > NOW() LIMIT 1",
            array('h' => $tokenHash)
        );
        if (!$fila) return null;
        // Defensa extra: comparacion en tiempo constante del hash
        return hash_equals($fila['token_hash'], $tokenHash) ? $fila : null;
    }

    /** IP del cliente (mismo patron que AuditHelper). */
    private function ipCliente() {
        if (!empty($_SERVER['HTTP_CLIENT_IP']))            return substr($_SERVER['HTTP_CLIENT_IP'], 0, 45);
        if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))      return substr($_SERVER['HTTP_X_FORWARDED_FOR'], 0, 45);
        if (isset($_SERVER['REMOTE_ADDR']))               return substr($_SERVER['REMOTE_ADDR'], 0, 45);
        return '127.0.0.1';
    }
}
