<?php

namespace AIPT\Api\Constants;

/**
 * Chatbot module constants with apply_filters hooks for site-level overrides.
 *
 * Every public constant has a corresponding static accessor that passes the
 * value through apply_filters(), making all limits overridable via standard
 * WordPress filter hooks without touching plugin code.
 *
 * Filter names are public API as of v2.6.0 — do NOT rename them.
 *
 * @package AIPT\Api\Constants
 * @since   2.6.0
 */
class ChatbotConstants {

    /**
     * Maximum chat message length in characters.
     *
     * Filter: aipt_chatbot_max_message_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_MESSAGE_LENGTH = 2000;

    /**
     * Maximum admin response length in characters.
     *
     * Filter: aipt_chatbot_max_response_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_RESPONSE_LENGTH = 5000;

    /**
     * Maximum user name length in characters.
     *
     * Filter: aipt_chatbot_max_user_name_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_USER_NAME_LENGTH = 100;

    /**
     * Maximum conversation history messages to accept per request.
     *
     * Filter: aipt_chatbot_max_history_messages
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_HISTORY_MESSAGES = 10;

    /**
     * Default items per page for message queue listing.
     *
     * Filter: aipt_chatbot_default_per_page
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_PAGINATION_PER_PAGE = 20;

    /**
     * Maximum items per page for message queue listing.
     *
     * Filter: aipt_chatbot_max_per_page
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_PAGINATION_PER_PAGE = 100;

    /**
     * Maximum items allowed in a single bulk-delete request.
     *
     * Filter: aipt_chatbot_max_bulk_delete
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_BULK_DELETE_ITEMS = 100;

    /**
     * Default maximum requests per rate-limit window.
     *
     * Filter: aipt_chatbot_default_rate_limit_requests
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_RATE_LIMIT_MAX_REQUESTS = 10;

    /**
     * Default rate-limit window in seconds.
     *
     * Filter: aipt_chatbot_default_rate_limit_window
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_RATE_LIMIT_WINDOW_SECONDS = 60;

    /**
     * Maximum allowed value for rate_limit.max_requests setting.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_RATE_LIMIT_MAX_REQUESTS = 200;

    /**
     * Minimum allowed value for rate_limit.window_seconds setting.
     *
     * @since 2.6.0
     * @var int
     */
    public const MIN_RATE_LIMIT_WINDOW_SECONDS = 10;

    /**
     * Maximum allowed value for rate_limit.window_seconds setting.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_RATE_LIMIT_WINDOW_SECONDS = 3600;

    /**
     * Default provider cooldown duration in seconds.
     *
     * Filter: aipt_chatbot_default_cooldown_seconds
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_PROVIDER_COOLDOWN_SECONDS = 60;

    /**
     * Minimum allowed provider cooldown in seconds.
     *
     * @since 2.6.0
     * @var int
     */
    public const MIN_PROVIDER_COOLDOWN_SECONDS = 5;

    /**
     * Maximum allowed provider cooldown in seconds.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_PROVIDER_COOLDOWN_SECONDS = 600;

    /**
     * Maximum bot name length in characters.
     *
     * Filter: aipt_chatbot_max_bot_name_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_BOT_NAME_LENGTH = 50;

    /**
     * Maximum greeting message length in characters.
     *
     * Filter: aipt_chatbot_max_greeting_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_GREETING_MESSAGE_LENGTH = 500;

    /**
     * Maximum store context length in characters.
     *
     * Filter: aipt_chatbot_max_store_context_length
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_STORE_CONTEXT_LENGTH = 5000;

    /**
     * Maximum custom label value length in characters.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_LABEL_LENGTH = 500;

    /**
     * Maximum generated custom CSS length in characters.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_GENERATED_CSS_LENGTH = 10000;

    /**
     * Maximum retry attempts for queued messages.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_RETRY_ATTEMPTS = 10;

    /**
     * Filter name that controls whether the chatbot uses the async
     * Action Scheduler pipeline. Default false until the operator flips
     * it per the 5-gate readiness checklist documented in the PR body.
     *
     * @since 2.6.1
     * @var string
     */
    public const ASYNC_ENABLED_FILTER = 'aipt_chatbot_async_enabled';

    /**
     * Worker time budget for ChatbotJobWorker::processJob, in seconds.
     * The CLAIM_TTL must always exceed this with a cushion so a worker
     * that runs to the limit cannot be reclaimed mid-flight.
     *
     * Filter: `aipt_chatbot_worker_timeout_seconds`.
     *
     * @since 2.6.1
     * @var int
     */
    public const WORKER_TIMEOUT_SECONDS = 90;

    /**
     * Stale-claim TTL (worker timeout + cushion), in seconds.
     *
     * Filter: `aipt_chatbot_claim_ttl_seconds`.
     *
     * @since 2.6.1
     * @var int
     */
    public const CLAIM_TTL_SECONDS = 180;

    /**
     * Length of the per-job status token used by GET /status/{job_uuid}.
     * Not filterable — shortening this would weaken the constant-time
     * hash_equals gate that protects the polling endpoint.
     *
     * @since 2.6.1
     * @var int
     */
    public const STATUS_TOKEN_LENGTH = 32;

    /**
     * Lock prefix for chatbot job state. Distinct from AutomationJobs'
     * `aipt_job_*` prefix so the two pipelines cannot collide on a
     * shared transient/option key.
     *
     * @since 2.6.1
     * @var string
     */
    public const JOB_LOCK_PREFIX = 'aipt_chatbot_job_';

    /**
     * Action Scheduler hook that ChatbotJobWorker::processJob handles.
     *
     * Filter: `aipt_chatbot_action_scheduler_hook`.
     *
     * @since 2.6.1
     * @var string
     */
    public const ACTION_SCHEDULER_HOOK = 'aipt_chatbot_process_job';

    /**
     * Action Scheduler hook that ChatbotJobWorker::reclaimStaleClaims handles.
     *
     * Filter: `aipt_chatbot_stale_recovery_hook`.
     *
     * @since 2.6.1
     * @var string
     */
    public const STALE_RECOVERY_HOOK = 'aipt_chatbot_reclaim_stale';

    /**
     * Recurring interval for the stale-recovery action, in seconds.
     *
     * Filter: `aipt_chatbot_stale_recovery_interval` — minimum 60s
     * enforced in the accessor to prevent operators from hot-looping
     * the database with an aggressive interval.
     *
     * @since 2.6.1
     * @var int
     */
    public const STALE_RECOVERY_INTERVAL_SECONDS = 300;

    /**
     * Additional grace period beyond expires_at before reclaim runs,
     * in seconds.
     *
     * Filter: `aipt_chatbot_stale_recovery_grace`.
     *
     * @since 2.6.1
     * @var int
     */
    public const STALE_RECOVERY_GRACE_SECONDS = 30;

    /**
     * Maximum products to include in a single AI response context.
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_PRODUCTS_PER_RESPONSE = 10;

    /**
     * Default maximum product context count for PromptBuilder.
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_PRODUCT_CONTEXT_MAX = 10;

    /**
     * Default age in days before chatbot messages are eligible for cleanup.
     *
     * @since 2.6.0
     * @var int
     */
    public const DEFAULT_CLEANUP_DAYS_OLD = 30;

    /**
     * Maximum characters per knowledge-page section in PromptBuilder.
     *
     * Filter: aipt_chatbot_max_chars_per_page
     *
     * @since 2.6.0
     * @var int
     */
    public const MAX_CHARS_PER_PAGE = 2000;

    /**
     * Get the effective maximum chat message length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxMessageLength(): int {
        /**
         * Filter the maximum chat message length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_message_length', self::MAX_MESSAGE_LENGTH );
        return $value >= 1 ? $value : self::MAX_MESSAGE_LENGTH;
    }

    /**
     * Get the effective maximum admin response length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxResponseLength(): int {
        /**
         * Filter the maximum admin response length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_response_length', self::MAX_RESPONSE_LENGTH );
        return $value >= 1 ? $value : self::MAX_RESPONSE_LENGTH;
    }

    /**
     * Get the effective maximum user name length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxUserNameLength(): int {
        /**
         * Filter the maximum user name length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_user_name_length', self::MAX_USER_NAME_LENGTH );
        return $value >= 1 ? $value : self::MAX_USER_NAME_LENGTH;
    }

    /**
     * Get the effective maximum history messages count.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxHistoryMessages(): int {
        /**
         * Filter the maximum conversation history messages per request.
         *
         * @since 2.6.0
         * @param int $count Default maximum message count.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_history_messages', self::MAX_HISTORY_MESSAGES );
        return $value >= 1 ? $value : self::MAX_HISTORY_MESSAGES;
    }

    /**
     * Get the effective default pagination per-page value.
     *
     * @since 2.6.0
     * @return int
     */
    public static function defaultPaginationPerPage(): int {
        /**
         * Filter the default items per page for message queue listing.
         *
         * @since 2.6.0
         * @param int $per_page Default items per page.
         */
        $value = (int) apply_filters( 'aipt_chatbot_default_per_page', self::DEFAULT_PAGINATION_PER_PAGE );
        return $value >= 1 ? $value : self::DEFAULT_PAGINATION_PER_PAGE;
    }

    /**
     * Get the effective maximum pagination per-page value.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxPaginationPerPage(): int {
        /**
         * Filter the maximum items per page for message queue listing.
         *
         * @since 2.6.0
         * @param int $per_page Default maximum items per page.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_per_page', self::MAX_PAGINATION_PER_PAGE );
        return $value >= 1 ? $value : self::MAX_PAGINATION_PER_PAGE;
    }

    /**
     * Get the effective maximum bulk-delete items count.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxBulkDeleteItems(): int {
        /**
         * Filter the maximum items allowed in a single bulk-delete request.
         *
         * @since 2.6.0
         * @param int $count Default maximum bulk delete count.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_bulk_delete', self::MAX_BULK_DELETE_ITEMS );
        return $value >= 1 ? $value : self::MAX_BULK_DELETE_ITEMS;
    }

    /**
     * Get the effective default rate-limit max requests.
     *
     * @since 2.6.0
     * @return int
     */
    public static function defaultRateLimitMaxRequests(): int {
        /**
         * Filter the default maximum requests per rate-limit window.
         *
         * @since 2.6.0
         * @param int $requests Default maximum requests.
         */
        $value = (int) apply_filters( 'aipt_chatbot_default_rate_limit_requests', self::DEFAULT_RATE_LIMIT_MAX_REQUESTS );
        if ( $value < 1 || $value > self::MAX_RATE_LIMIT_MAX_REQUESTS ) {
            return self::DEFAULT_RATE_LIMIT_MAX_REQUESTS;
        }
        return $value;
    }

    /**
     * Get the effective default rate-limit window in seconds.
     *
     * @since 2.6.0
     * @return int
     */
    public static function defaultRateLimitWindowSeconds(): int {
        /**
         * Filter the default rate-limit window in seconds.
         *
         * @since 2.6.0
         * @param int $seconds Default window duration in seconds.
         */
        $value = (int) apply_filters( 'aipt_chatbot_default_rate_limit_window', self::DEFAULT_RATE_LIMIT_WINDOW_SECONDS );
        if ( $value < self::MIN_RATE_LIMIT_WINDOW_SECONDS || $value > self::MAX_RATE_LIMIT_WINDOW_SECONDS ) {
            return self::DEFAULT_RATE_LIMIT_WINDOW_SECONDS;
        }
        return $value;
    }

    /**
     * Get the effective default provider cooldown in seconds.
     *
     * @since 2.6.0
     * @return int
     */
    public static function defaultProviderCooldownSeconds(): int {
        /**
         * Filter the default provider cooldown duration in seconds.
         *
         * @since 2.6.0
         * @param int $seconds Default cooldown duration in seconds.
         */
        $value = (int) apply_filters( 'aipt_chatbot_default_cooldown_seconds', self::DEFAULT_PROVIDER_COOLDOWN_SECONDS );
        if ( $value < self::MIN_PROVIDER_COOLDOWN_SECONDS || $value > self::MAX_PROVIDER_COOLDOWN_SECONDS ) {
            return self::DEFAULT_PROVIDER_COOLDOWN_SECONDS;
        }
        return $value;
    }

    /**
     * Get the effective maximum bot name length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxBotNameLength(): int {
        /**
         * Filter the maximum bot name length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_bot_name_length', self::MAX_BOT_NAME_LENGTH );
        return $value >= 1 ? $value : self::MAX_BOT_NAME_LENGTH;
    }

    /**
     * Get the effective maximum greeting message length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxGreetingMessageLength(): int {
        /**
         * Filter the maximum greeting message length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_greeting_length', self::MAX_GREETING_MESSAGE_LENGTH );
        return $value >= 1 ? $value : self::MAX_GREETING_MESSAGE_LENGTH;
    }

    /**
     * Get the effective maximum store context length.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxStoreContextLength(): int {
        /**
         * Filter the maximum store context length.
         *
         * @since 2.6.0
         * @param int $length Default maximum length in characters.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_store_context_length', self::MAX_STORE_CONTEXT_LENGTH );
        return $value >= 1 ? $value : self::MAX_STORE_CONTEXT_LENGTH;
    }

    /**
     * Get the effective maximum characters per knowledge-page section.
     *
     * @since 2.6.0
     * @return int
     */
    public static function maxCharsPerPage(): int {
        /**
         * Filter the maximum characters per knowledge-page section in PromptBuilder.
         *
         * @since 2.6.0
         * @param int $chars Default maximum characters per page.
         */
        $value = (int) apply_filters( 'aipt_chatbot_max_chars_per_page', self::MAX_CHARS_PER_PAGE );
        return $value >= 1 ? $value : self::MAX_CHARS_PER_PAGE;
    }

    /**
     * Effective async-chat enabled flag.
     *
     * Filter: `aipt_chatbot_async_enabled` — default false; the operator
     * flips this once the 5-gate readiness checklist passes.
     *
     * @since 2.6.1
     */
    public static function isAsyncEnabled(): bool {
        return (bool) apply_filters( self::ASYNC_ENABLED_FILTER, false );
    }

    /**
     * Effective worker time budget in seconds.
     *
     * Filter: `aipt_chatbot_worker_timeout_seconds`.
     *
     * @since 2.6.1
     */
    public static function workerTimeoutSeconds(): int {
        $value = (int) apply_filters( 'aipt_chatbot_worker_timeout_seconds', self::WORKER_TIMEOUT_SECONDS );
        return $value >= 1 ? $value : self::WORKER_TIMEOUT_SECONDS;
    }

    /**
     * Effective claim TTL in seconds. Floor enforced so this never
     * drops below the worker timeout (otherwise a healthy worker
     * could be reclaimed mid-flight).
     *
     * Filter: `aipt_chatbot_claim_ttl_seconds`.
     *
     * @since 2.6.1
     */
    public static function claimTtlSeconds(): int {
        $value = (int) apply_filters( 'aipt_chatbot_claim_ttl_seconds', self::CLAIM_TTL_SECONDS );
        $floor = self::workerTimeoutSeconds();

        return $value >= $floor ? $value : $floor;
    }

    /**
     * Status-token length. Intentionally NOT filterable: shrinking the
     * token weakens the constant-time hash_equals gate that protects
     * the /status endpoint.
     *
     * @since 2.6.1
     */
    public static function statusTokenLength(): int {
        return self::STATUS_TOKEN_LENGTH;
    }

    /**
     * Lock prefix. Intentionally NOT filterable: customising the prefix
     * would let chatbot transients collide with AutomationJobs.
     *
     * @since 2.6.1
     */
    public static function jobLockPrefix(): string {
        return self::JOB_LOCK_PREFIX;
    }

    /**
     * Effective Action Scheduler hook for ChatbotJobWorker::processJob.
     *
     * Filter: `aipt_chatbot_action_scheduler_hook`.
     *
     * @since 2.6.1
     */
    public static function actionSchedulerHook(): string {
        $value = (string) apply_filters( 'aipt_chatbot_action_scheduler_hook', self::ACTION_SCHEDULER_HOOK );
        return '' !== $value ? $value : self::ACTION_SCHEDULER_HOOK;
    }

    /**
     * Effective stale-recovery hook for
     * ChatbotJobWorker::reclaimStaleClaims.
     *
     * Filter: `aipt_chatbot_stale_recovery_hook`.
     *
     * @since 2.6.1
     */
    public static function staleRecoveryHook(): string {
        $value = (string) apply_filters( 'aipt_chatbot_stale_recovery_hook', self::STALE_RECOVERY_HOOK );
        return '' !== $value ? $value : self::STALE_RECOVERY_HOOK;
    }

    /**
     * Effective stale-recovery interval, in seconds. Floor at 60 to
     * prevent operators from hot-looping the database.
     *
     * Filter: `aipt_chatbot_stale_recovery_interval`.
     *
     * @since 2.6.1
     */
    public static function staleRecoveryIntervalSeconds(): int {
        $value = (int) apply_filters( 'aipt_chatbot_stale_recovery_interval', self::STALE_RECOVERY_INTERVAL_SECONDS );
        return $value >= 60 ? $value : self::STALE_RECOVERY_INTERVAL_SECONDS;
    }

    /**
     * Effective stale-recovery grace period, in seconds.
     *
     * Filter: `aipt_chatbot_stale_recovery_grace`.
     *
     * @since 2.6.1
     */
    public static function staleRecoveryGraceSeconds(): int {
        $value = (int) apply_filters( 'aipt_chatbot_stale_recovery_grace', self::STALE_RECOVERY_GRACE_SECONDS );
        return $value >= 0 ? $value : self::STALE_RECOVERY_GRACE_SECONDS;
    }
}
