<?php

namespace WPQT\Session;

use WPQT\Services\ServiceLocator;
use WPQT\WPQTException;

if (!defined('ABSPATH')) {
    exit;
}

if (!class_exists('WPQT\Session\SessionService')) {
    class SessionService
    {
        /**
         * Calculates the expiry date for a new token.
         *
         * This method calculates the expiry date for a new token by adding one hour to the current UTC time.
         *
         * @return string The expiry date in 'Y-m-d H:i:s' format in UTC.
         */
        public function getNewTokenExpiryDate()
        {
            // Get the current UTC time
            $current_time = time();

            // Calculate the expiry date
            $expiry_time = $current_time + WP_QUICKTASKER_SESSION_LENGHT;

            // Return the expiry date in 'Y-m-d H:i:s' format in UTC
            return gmdate('Y-m-d H:i:s', $expiry_time);
        }

        /**
         * Generates a session token.
         *
         * @return string The generated session token.
         */
        public function generateSessionToken()
        {
            return bin2hex(random_bytes(32));
        }

        /**
         * Creates a new session for a user.
         *
         * @param int $userId The ID of the user.
         * @param string $userPageHash The page hash associated with the user.
         * @return UserSession The newly created user session.
         * @throws WPQTException If failed to delete existing session or create new session.
         */
        public function createSession($userId, $userPageHash)
        {
            global $wpdb;

            $plaintextToken = $this->generateSessionToken();

            $result = $wpdb->insert(
                TABLE_WP_QUICKTASKER_USER_SESSIONS,
                [
                    'user_id'        => $userId,
                    'page_hash'      => $userPageHash,
                    'created_at_utc' => ServiceLocator::get('TimeRepository')->getCurrentUTCTime(),
                    'expires_at_utc' => $this->getNewTokenExpiryDate(),
                    'session_token'  => SessionRepository::hashToken($plaintextToken),
                ]
            );

            if (false === $result) {
                throw new WPQTException('Failed to create new session');
            }

            $session = ServiceLocator::get('SessionRepository')->getUserSessionById($wpdb->insert_id);

            // Hand the plaintext token back to the caller (and only this once) so it
            // can be delivered to the client. The DB row keeps only the hash.
            $session->session_token = $plaintextToken;

            return $session;
        }

        /**
         * Deletes a session by marking it as inactive in the database.
         *
         * This function updates the `is_active` field to 0 for the session
         * identified by the provided session token.
         *
         * @param string $sessionToken The token of the session to be marked as inactive.
         * @return bool Returns true if the session was successfully marked as inactive.
         * @throws WPQTException If the session could not be marked as inactive.
         */
        public function markSessionInactive($sessionToken)
        {
            global $wpdb;

            $result = $wpdb->update(
                TABLE_WP_QUICKTASKER_USER_SESSIONS,
                [
                    'is_active' => 0
                ],
                [
                    'session_token' => SessionRepository::hashToken($sessionToken)
                ]
            );

            if (false === $result) {
                throw new WPQTException('Failed to mark session as not active', true);
            }

            return true;
        }

        /**
         * Logs out the current WordPress user by clearing the authentication cookie.
         *
         * This function is used to log out the current user from the WordPress session.
         */
        public function logOutCurrentWPUser()
        {
            wp_clear_auth_cookie();
        }

        /**
         * Verifies the session token.
         *
         * @throws \Exception If the session token is invalid or has expired.
         *
         * @return Session|null The session object if the token is valid and has not expired, null otherwise.
         */
        public function verifySessionToken($pageHash)
        {
            $sessionToken = sanitize_text_field($_COOKIE['wpqt-session-token-' . $pageHash]);
            $session = ServiceLocator::get('SessionRepository')->getUserSession($sessionToken);

            if (null === $session) {
                throw new WPQTException(esc_html(WP_QUICKTASKER_INVALID_SESSION_TOKEN), true);
            }

            if ($pageHash !== $session->page_hash) {
                throw new WPQTException('Session token does not match the page hash', true);
            }

            if ('0' === $session->is_active) {
                throw new WPQTException('Session is not active', true);
            }

            if (strtotime($session->expires_at_utc) < time()) {
                throw new WPQTException('Session has expired', true);
            }

            return $session;
        }
    }
}
