<?php
/**
 * ASISMA CRM - Helper de Gestión de Sesión
 */
class SessionHelper {

    public static function startSession($userRow) {
        $_SESSION['usuario_id']           = $userRow['id'];
        $_SESSION['usuario_nombre']       = $userRow['nombre'];
        $_SESSION['usuario_email']        = $userRow['email'];
        $_SESSION['usuario_rol']          = $userRow['rol'];
        $_SESSION['usuario_empresa_id']   = $userRow['empresa_id'];
        $_SESSION['usuario_franquiciado_id'] = $userRow['franquiciado_id'];
        $_SESSION['last_activity']        = time();

        $permisos = array();
        if (!empty($userRow['permisos'])) {
            if (is_string($userRow['permisos'])) {
                $decoded = json_decode($userRow['permisos'], true);
                $permisos = $decoded ? $decoded : array();
            } elseif (is_array($userRow['permisos'])) {
                $permisos = $userRow['permisos'];
            }
        }
        $_SESSION['usuario_permisos']          = $permisos;
        $_SESSION['franquiciado_vertical_id']  = isset($userRow['franquiciado_vertical_id']) ? $userRow['franquiciado_vertical_id'] : null;
    }

    public static function checkSession() {
        // 1. ¿Sesión PHP activa y dentro de las 12h?
        if (isset($_SESSION['usuario_id'])) {
            $lastActivity = isset($_SESSION['last_activity']) ? $_SESSION['last_activity'] : 0;
            if (time() - $lastActivity <= SESSION_TIMEOUT) {
                $_SESSION['last_activity'] = time();
                return true;
            }
        }
        // 2. Sin sesión PHP (cerró el navegador o expiraron las 12h): si el
        //    usuario marcó "Recordar dispositivo", se revive con la cookie.
        return self::intentarLoginRecordado();
    }

    public static function destroySession() {
        // Cerrar sesión también olvida ESTE dispositivo (borra su cookie remember)
        self::olvidarDispositivo();

        if (session_status() == PHP_SESSION_ACTIVE) {
            $_SESSION = array();
            if (ini_get("session.use_cookies")) {
                $params = session_get_cookie_params();
                setcookie(
                    session_name(),
                    '',
                    time() - 42000,
                    $params["path"],
                    $params["domain"],
                    $params["secure"],
                    $params["httponly"]
                );
            }
            session_destroy();
        }
    }

    // =====================================================================
    // "RECORDAR DISPOSITIVO" (login persistente 30 días)
    // =====================================================================

    /**
     * Crea el token persistente y deja la cookie. Se llama al iniciar sesión
     * cuando el usuario marcó "Recordar dispositivo".
     */
    public static function crearTokenRecordado($usuarioId) {
        $selector  = bin2hex(openssl_random_pseudo_bytes(12)); // 24 chars, publico
        $validator = bin2hex(openssl_random_pseudo_bytes(32)); // 64 chars, secreto

        $db = Database::getInstance();
        // Vencimiento con el reloj de MySQL (PHP y MySQL van en timezones distintos)
        $db->query(
            "INSERT INTO sesiones_recordadas
                (usuario_id, selector, validator_hash, expira_at, user_agent, ip, ultimo_uso_at)
             VALUES (:uid, :sel, :val, DATE_ADD(NOW(), INTERVAL :dias DAY), :ua, :ip, NOW())",
            array(
                'uid'  => (int)$usuarioId,
                'sel'  => $selector,
                'val'  => hash('sha256', $validator),
                'dias' => REMEMBER_DIAS,
                'ua'   => isset($_SERVER['HTTP_USER_AGENT']) ? substr($_SERVER['HTTP_USER_AGENT'], 0, 255) : null,
                'ip'   => self::ipCliente()
            )
        );

        self::setCookieRecordar($selector . ':' . $validator, time() + REMEMBER_DIAS * 86400);
    }

    /**
     * Revive la sesión desde la cookie "Recordar dispositivo". Devuelve true si
     * reautenticó. Extiende el vencimiento en cada uso (expiración deslizante).
     */
    private static function intentarLoginRecordado() {
        if (empty($_COOKIE[REMEMBER_COOKIE])) return false;

        $partes = explode(':', $_COOKIE[REMEMBER_COOKIE], 2);
        if (count($partes) !== 2) { self::olvidarDispositivo(); return false; }
        list($selector, $validator) = $partes;

        // Formato estricto (selector 24 hex, validator 64 hex)
        if (strlen($selector) !== 24 || !ctype_xdigit($selector)
            || strlen($validator) !== 64 || !ctype_xdigit($validator)) {
            self::olvidarDispositivo();
            return false;
        }

        $db  = Database::getInstance();
        $fila = $db->fetchOne(
            "SELECT id, usuario_id, validator_hash FROM sesiones_recordadas
             WHERE selector = :sel AND expira_at > NOW() LIMIT 1",
            array('sel' => $selector)
        );
        if (!$fila) { self::olvidarDispositivo(); return false; }

        // Comparación del validator en tiempo constante
        if (!hash_equals($fila['validator_hash'], hash('sha256', $validator))) {
            // El selector existe pero el secreto no calza: cookie manipulada o
            // robada. Se invalida esta sesión recordada por seguridad.
            $db->query("DELETE FROM sesiones_recordadas WHERE id = :id", array('id' => (int)$fila['id']));
            self::olvidarDispositivo();
            return false;
        }

        // Usuario debe existir y seguir activo
        $user = self::cargarUsuarioActivo((int)$fila['usuario_id']);
        if (!$user) {
            $db->query("DELETE FROM sesiones_recordadas WHERE id = :id", array('id' => (int)$fila['id']));
            self::olvidarDispositivo();
            return false;
        }

        // Revivir la sesión y extender la vigencia del recordatorio
        self::startSession($user);
        $db->query(
            "UPDATE sesiones_recordadas
             SET expira_at = DATE_ADD(NOW(), INTERVAL :dias DAY), ultimo_uso_at = NOW(), ip = :ip
             WHERE id = :id",
            array('dias' => REMEMBER_DIAS, 'ip' => self::ipCliente(), 'id' => (int)$fila['id'])
        );
        self::setCookieRecordar($_COOKIE[REMEMBER_COOKIE], time() + REMEMBER_DIAS * 86400);

        return true;
    }

    /** Borra la sesión recordada del dispositivo actual (fila en BD + cookie). */
    private static function olvidarDispositivo() {
        if (!empty($_COOKIE[REMEMBER_COOKIE])) {
            $partes = explode(':', $_COOKIE[REMEMBER_COOKIE], 2);
            if (!empty($partes[0]) && ctype_xdigit($partes[0])) {
                try {
                    Database::getInstance()->query(
                        "DELETE FROM sesiones_recordadas WHERE selector = :sel",
                        array('sel' => $partes[0])
                    );
                } catch (Exception $e) {
                    // no bloquear el logout por un fallo al limpiar la fila
                }
            }
            self::setCookieRecordar('', time() - 42000); // expira la cookie
        }
    }

    /** Carga el usuario activo con los campos que necesita startSession. */
    private static function cargarUsuarioActivo($usuarioId) {
        return Database::getInstance()->fetchOne(
            "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.id = :id AND u.activo = 1 LIMIT 1",
            array('id' => (int)$usuarioId)
        );
    }

    /** Set/borra la cookie remember (httponly, secure en https). */
    private static function setCookieRecordar($valor, $expira) {
        $secure = (strpos(APP_URL, 'https://') === 0);
        setcookie(REMEMBER_COOKIE, $valor, $expira, '/', '', $secure, true);
        // Refleja el cambio en la request actual
        if ($valor === '') {
            unset($_COOKIE[REMEMBER_COOKIE]);
        } else {
            $_COOKIE[REMEMBER_COOKIE] = $valor;
        }
    }

    private static 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';
    }

    public static function getUsuarioId() {
        return isset($_SESSION['usuario_id']) ? $_SESSION['usuario_id'] : null;
    }

    public static function getNombre() {
        return isset($_SESSION['usuario_nombre']) ? $_SESSION['usuario_nombre'] : 'Invitado';
    }

    public static function getEmail() {
        return isset($_SESSION['usuario_email']) ? $_SESSION['usuario_email'] : '';
    }

    public static function getRol() {
        return isset($_SESSION['usuario_rol']) ? $_SESSION['usuario_rol'] : '';
    }

    public static function getEmpresaId() {
        return isset($_SESSION['usuario_empresa_id']) ? $_SESSION['usuario_empresa_id'] : null;
    }

    public static function getFranquiciadoId() {
        return isset($_SESSION['usuario_franquiciado_id']) ? $_SESSION['usuario_franquiciado_id'] : null;
    }

    /**
     * Nombre de fantasía de la franquicia (o matriz) del usuario activo.
     * Se consulta una sola vez y queda cacheado en la sesión, así sirve
     * también para sesiones iniciadas antes de este cambio.
     */
    public static function getFranquiciaNombre() {
        if (!isset($_SESSION['usuario_id'])) {
            return '';
        }
        if (!empty($_SESSION['franquicia_nombre'])) {
            return $_SESSION['franquicia_nombre'];
        }
        $fila = Database::getInstance()->fetchOne(
            "SELECT nombre_fantasia, razon_social FROM empresas_franquiciados WHERE id = :id",
            array('id' => (int)self::getEmpresaId())
        );
        $nombre = '';
        if ($fila) {
            $nombre = $fila['nombre_fantasia'] !== '' && $fila['nombre_fantasia'] !== null
                ? $fila['nombre_fantasia']
                : $fila['razon_social'];
        }
        $_SESSION['franquicia_nombre'] = $nombre;
        return $nombre;
    }

    public static function getPermisosArray() {
        return isset($_SESSION['usuario_permisos']) ? $_SESSION['usuario_permisos'] : array();
    }

    public static function getVerticalId() {
        return isset($_SESSION['franquiciado_vertical_id']) ? $_SESSION['franquiciado_vertical_id'] : null;
    }

    public static function isAdmin() {
        return self::getRol() === ROL_ADMIN;
    }

    public static function hasPermiso($modulo) {
        $rol = self::getRol();

        if ($rol === ROL_ADMIN) {
            return true;
        }

        $permisosPorRol = array(
            // Mantenimiento de catálogo (verticales/marcas) solo admin matriz;
            // el franquiciado solo ve los productos que tiene autorizados.
            ROL_FRANQUICIADO => array(
                'dashboard', 'leads', 'cotizaciones', 'empresas', 'contactos',
                'whatsapp', 'operaciones', 'reportes', 'configuracion',
                'productos', 'usuarios',
            ),
            ROL_VENDEDOR => array(
                'dashboard', 'leads', 'cotizaciones', 'empresas', 'contactos',
                'whatsapp',
            ),
            ROL_MARCA => array(
                'dashboard', 'operaciones',
            ),
        );

        if (!isset($permisosPorRol[$rol])) {
            return false;
        }

        return in_array($modulo, $permisosPorRol[$rol]);
    }
}
