<?php
/**
 * Cast Conductor Proprietary License v5
 * SPDX-License-Identifier: LicenseRef-CastConductor-Proprietary-v5
 * 
 * Copyright (c) 2025 CastConductor.com. All Rights Reserved.
 * See LICENSE and EULA-v5.2.md for full terms.
 */

/**
 * CastConductor Analytics Helpers
 * 
 * Utility functions for analytics including tier checking,
 * feature gating, and common operations.
 * 
 * @package CastConductor
 * @subpackage Analytics
 * @since 5.8.0
 */

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

class CC_Analytics_Helpers {

    /**
     * Plan tiers in order
     */
    const TIERS = ['creator', 'pro', 'business', 'agency', 'enterprise'];

    /**
     * Feature availability map
     * Maps feature keys to the minimum tier required
     */
    const FEATURE_MAP = [
        // Available to all tiers
        'basic_stats'      => ['creator', 'pro', 'business', 'agency', 'enterprise'],
        'content_perf'     => ['creator', 'pro', 'business', 'agency', 'enterprise'],
        'device_breakdown' => ['creator', 'pro', 'business', 'agency', 'enterprise'],
        'csv_export'       => ['creator', 'pro', 'business', 'agency', 'enterprise'],
        
        // Pro and above
        'geo_heatmap'      => ['pro', 'business', 'agency', 'enterprise'],
        'qr_tracking'      => ['pro', 'business', 'agency', 'enterprise'],
        
        // Business and above
        'sponsor_reports'  => ['business', 'agency', 'enterprise'],
        'realtime'         => ['business', 'agency', 'enterprise'],
        'pdf_export'       => ['business', 'agency', 'enterprise'],
        'webhooks'         => ['business', 'agency', 'enterprise'],
        'partner_api'      => ['business', 'agency', 'enterprise'],
        'segment'          => ['business', 'agency', 'enterprise'],
        
        // Agency and above
        'white_label'      => ['agency', 'enterprise'],
        'multi_channel'    => ['agency', 'enterprise'],
        
        // Enterprise only
        'ab_testing'       => ['enterprise'],
    ];

    /**
     * Default retention settings (in days, 0 = forever)
     */
    const DEFAULT_RETENTION = [
        'raw_events' => 7,
        'sessions'   => 30,
        'hourly'     => 90,
        'daily'      => 0,  // forever
        'sponsor'    => 0,  // forever
    ];

    /**
     * Retention limits
     */
    const RETENTION_LIMITS = [
        'raw_events' => ['min' => 1,   'max' => 30],
        'sessions'   => ['min' => 7,   'max' => 90],
        'hourly'     => ['min' => 30,  'max' => 365],
        'daily'      => ['min' => 365, 'max' => 0],  // 0 = forever
        'sponsor'    => ['min' => 365, 'max' => 0],  // 0 = forever
    ];

    /**
     * Content block types that can be tracked
     * Format: type_key => [label, default_enabled, description]
     */
    const CONTENT_BLOCK_TYPES = [
        'promo'           => ['Promos', true, 'Promotional content blocks'],
        'sponsor'         => ['Sponsors', true, 'Sponsor content blocks'],
        'shoutout'        => ['Shoutouts', true, 'Shoutout content blocks'],
        'track_info'      => ['Track Info', false, 'Current playing track info (high volume)'],
        'track_info_hero' => ['Track Info Hero', false, 'Track info hero blocks (high volume)'],
        'weather'         => ['Weather', false, 'Weather content blocks'],
        'clock'           => ['Clock/Time', false, 'Clock and time content blocks'],
        'location'        => ['Location', false, 'Location content blocks'],
        'location_time'   => ['Location & Time', false, 'Location and time content blocks'],
        'custom'          => ['Custom', true, 'Custom content blocks'],
        'custom_api'      => ['Custom API', true, 'Custom API content blocks'],
    ];

    /**
     * Check if current license has a specific analytics feature
     * 
     * @param string $feature Feature key (e.g., 'geo_heatmap', 'qr_tracking')
     * @return bool
     */
    public static function has_feature($feature) {
        $plan = self::get_current_plan();
        
        if (!isset(self::FEATURE_MAP[$feature])) {
            return false;
        }
        
        return in_array($plan, self::FEATURE_MAP[$feature], true);
    }

    /**
     * Get current license plan
     * 
     * Normalizes plan names from the license API to analytics tier names.
     * Handles legacy 'basic' and special 'master' plans.
     * 
     * @return string Plan slug (creator, pro, business, agency, enterprise, or 'none')
     */
    public static function get_current_plan() {
        if (!class_exists('CastConductor_License_Manager')) {
            return 'none';
        }
        
        $license_manager = CastConductor_License_Manager::instance();
        $raw_plan = $license_manager->get_plan();
        
        // Normalize plan names from API to analytics tiers
        return self::normalize_plan($raw_plan);
    }

    /**
     * Normalize plan name from license API to analytics tier
     * 
     * @param string $plan Raw plan name from license API
     * @return string Normalized tier name
     */
    public static function normalize_plan($plan) {
        // Map of API plan names to analytics tiers
        $plan_map = [
            // Master keys get enterprise (all features)
            'master'     => 'enterprise',
            
            // Legacy 'basic' maps to creator (lowest paid tier)
            'basic'      => 'creator',
            
            // Standard tiers pass through
            'creator'    => 'creator',
            'pro'        => 'pro',
            'business'   => 'business',
            'agency'     => 'agency',
            'enterprise' => 'enterprise',
        ];
        
        $normalized = $plan_map[strtolower($plan)] ?? 'none';
        
        return $normalized;
    }

    /**
     * Get all features available for current plan
     * 
     * @return array List of available feature keys
     */
    public static function get_available_features() {
        $plan = self::get_current_plan();
        $available = [];
        
        foreach (self::FEATURE_MAP as $feature => $tiers) {
            if (in_array($plan, $tiers, true)) {
                $available[] = $feature;
            }
        }
        
        return $available;
    }

    /**
     * Get retention setting with smart defaults
     * 
     * @param string $type Type of data (raw_events, sessions, hourly, daily, sponsor)
     * @return int Retention in days (0 = forever)
     */
    public static function get_retention_days($type) {
        $settings = get_option('cc_analytics_retention', []);
        
        if (isset($settings[$type])) {
            return self::validate_retention($type, (int) $settings[$type]);
        }
        
        return self::DEFAULT_RETENTION[$type] ?? 7;
    }

    /**
     * Validate retention value against limits
     * 
     * @param string $type Type of data
     * @param int $days Desired retention days
     * @return int Validated retention days
     */
    public static function validate_retention($type, $days) {
        if (!isset(self::RETENTION_LIMITS[$type])) {
            return $days;
        }
        
        $limits = self::RETENTION_LIMITS[$type];
        
        // 0 means forever (no limit)
        if ($limits['max'] === 0 && $days === 0) {
            return 0;
        }
        
        // Apply min/max bounds
        if ($days < $limits['min']) {
            return $limits['min'];
        }
        
        if ($limits['max'] > 0 && $days > $limits['max']) {
            return $limits['max'];
        }
        
        return $days;
    }

    /**
     * Get all distinct content block types from the database
     * 
     * @return array List of type strings
     */
    public static function get_database_block_types() {
        global $wpdb;
        $table = $wpdb->prefix . 'castconductor_content_blocks';
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
        $types = $wpdb->get_col("SELECT DISTINCT type FROM {$table} WHERE type IS NOT NULL AND type != ''");
        
        return is_array($types) ? $types : [];
    }

    /**
     * Get collection settings for content block types
     * Includes both hardcoded known types and user-created types from database
     * 
     * @return array Map of content_block_type => enabled (bool)
     */
    public static function get_collection_settings() {
        $settings = get_option('cc_analytics_collection', []);
        
        $result = [];
        
        // First, add hardcoded known types with their defaults
        foreach (self::CONTENT_BLOCK_TYPES as $type => $config) {
            // Use saved setting or default
            $result[$type] = isset($settings[$type]) 
                ? (bool) $settings[$type] 
                : $config[1]; // default_enabled is index 1
        }
        
        // Then, add any database types that aren't in the hardcoded list
        $db_types = self::get_database_block_types();
        foreach ($db_types as $type) {
            if (!isset($result[$type])) {
                // User-created type: use saved setting or default to true (collect)
                $result[$type] = isset($settings[$type]) ? (bool) $settings[$type] : true;
            }
        }
        
        return $result;
    }

    /**
     * Check if a content block type should be collected
     * 
     * @param string $block_type Content block type (e.g., 'track_info', 'promo')
     * @return bool Whether to collect impressions for this type
     */
    public static function should_collect_type($block_type) {
        if (empty($block_type)) {
            return true; // Collect unknown types by default
        }
        
        $settings = self::get_collection_settings();
        
        // Normalize type name (track_info_hero -> track_info_hero, but also handle variants)
        $normalized = strtolower(str_replace('-', '_', $block_type));
        
        // If type is explicitly configured, use that setting
        if (isset($settings[$normalized])) {
            return $settings[$normalized];
        }
        
        // For unknown types, check if 'custom' is enabled
        return $settings['custom'] ?? true;
    }

    /**
     * Update collection settings
     * Accepts any type including user-created content block types
     * 
     * @param array $settings Map of content_block_type => enabled (bool)
     * @return bool Success
     */
    public static function update_collection_settings($settings) {
        $validated = [];
        
        // Get all valid types (hardcoded + database)
        $all_types = self::get_collection_settings();
        
        // Validate and save any type that's either known or in database
        foreach ($settings as $type => $enabled) {
            // Normalize the type
            $normalized = strtolower(str_replace('-', '_', $type));
            // Only accept types that exist in our combined list
            if (isset($all_types[$normalized]) || isset(self::CONTENT_BLOCK_TYPES[$normalized])) {
                $validated[$normalized] = (bool) $enabled;
            } else {
                // Also accept types that exist in the database
                $db_types = self::get_database_block_types();
                if (in_array($normalized, $db_types, true)) {
                    $validated[$normalized] = (bool) $enabled;
                }
            }
        }
        
        return update_option('cc_analytics_collection', $validated);
    }

    /**
     * Get all available content block types with metadata
     * Includes both hardcoded known types and user-created types from database
     * 
     * @return array Array of type info with label, enabled, description
     */
    public static function get_content_block_type_info() {
        $settings = self::get_collection_settings();
        
        $result = [];
        
        // First, add hardcoded known types with their metadata
        foreach (self::CONTENT_BLOCK_TYPES as $type => $config) {
            $result[$type] = [
                'label'       => $config[0],
                'enabled'     => $settings[$type] ?? $config[1],
                'description' => $config[2],
            ];
        }
        
        // Then, add any database types that aren't in the hardcoded list
        $db_types = self::get_database_block_types();
        foreach ($db_types as $type) {
            if (!isset($result[$type])) {
                // Generate label from type name (e.g., 'my_custom_block' -> 'My Custom Block')
                $label = ucwords(str_replace(['_', '-'], ' ', $type));
                $result[$type] = [
                    'label'       => $label,
                    'enabled'     => $settings[$type] ?? true, // Default to enabled
                    'description' => 'User-created content block',
                ];
            }
        }
        
        return $result;
    }

    /**
     * Generate a secure session ID
     * 
     * @return string Session ID prefixed with 'sess_'
     */
    public static function generate_session_id() {
        return 'sess_' . bin2hex(random_bytes(16));
    }

    /**
     * Hash a device ID for privacy
     * 
     * @param string $device_id Raw device ID
     * @return string SHA-256 hashed device ID
     */
    public static function hash_device_id($device_id) {
        // Add a site-specific salt for extra security
        $salt = wp_salt('auth');
        return hash('sha256', $device_id . $salt);
    }

    /**
     * Get the analytics API key (auto-generated if not exists)
     * 
     * @return string Analytics API key
     */
    public static function get_analytics_key() {
        $key = get_option('cc_analytics_key');
        
        if (empty($key)) {
            $key = self::generate_analytics_key();
            update_option('cc_analytics_key', $key);
        }
        
        return $key;
    }

    /**
     * Generate a new analytics API key
     * 
     * @return string New analytics key
     */
    public static function generate_analytics_key() {
        return 'cc_analytics_live_' . wp_generate_password(32, false, false);
    }

    /**
     * Regenerate the analytics API key
     * 
     * @return string New analytics key
     */
    public static function regenerate_analytics_key() {
        $key = self::generate_analytics_key();
        update_option('cc_analytics_key', $key);
        return $key;
    }

    /**
     * Convert ISO 8601 timestamp to MySQL datetime
     * 
     * @param string $iso_timestamp ISO 8601 timestamp
     * @return string MySQL datetime format
     */
    public static function iso_to_mysql($iso_timestamp) {
        $dt = new DateTime($iso_timestamp);
        $dt->setTimezone(new DateTimeZone('UTC'));
        return $dt->format('Y-m-d H:i:s');
    }

    /**
     * Get current UTC datetime in MySQL format
     * 
     * @return string MySQL datetime
     */
    public static function now_utc() {
        return gmdate('Y-m-d H:i:s');
    }

    /**
     * Validate event type against allowed types
     * 
     * @param string $event_type Event type to validate
     * @return bool
     */
    public static function is_valid_event_type($event_type) {
        $valid_types = [
            // Session events
            'session_start',
            'session_restoration',
            'session_heartbeat',
            'session_end',
            'session_geo_update',
            'session_scene_update',
            // Content events
            'scene_view',
            'scene_dwell',
            'container_impression',
            'promo_impression',
            'impression_batch', // Batched impressions (multiple counts in one event)
            // Interaction events
            'qr_scan',
            'navigation',
            'button_press',
            // Stream events
            'stream_start',
            'stream_buffer',
            'stream_error',
            'stream_end',
        ];
        
        return in_array($event_type, $valid_types, true);
    }

    /**
     * Get upgrade prompt for a locked feature
     * 
     * @param string $feature Feature key
     * @return array Upgrade info with required tier and message
     */
    public static function get_upgrade_prompt($feature) {
        if (!isset(self::FEATURE_MAP[$feature])) {
            return null;
        }
        
        $required_tiers = self::FEATURE_MAP[$feature];
        $required_tier = $required_tiers[0]; // First tier that has access
        
        $messages = [
            'geo_heatmap'     => 'Upgrade to Pro to see where your viewers are located.',
            'qr_tracking'     => 'Upgrade to Pro to track QR code scans and prove sponsor ROI.',
            'sponsor_reports' => 'Upgrade to Business to generate professional sponsor reports.',
            'realtime'        => 'Upgrade to Business for real-time analytics.',
            'pdf_export'      => 'Upgrade to Business to export PDF reports for sponsors.',
            'webhooks'        => 'Upgrade to Business to push analytics to external systems.',
            'partner_api'     => 'Upgrade to Business to enable partner API access.',
            'segment'         => 'Upgrade to Business to integrate with Segment.',
            'white_label'     => 'Upgrade to Agency for white-label reports.',
            'multi_channel'   => 'Upgrade to Agency to manage analytics across multiple channels.',
            'ab_testing'      => 'Upgrade to Enterprise for A/B testing capabilities.',
        ];
        
        return [
            'required_tier' => $required_tier,
            'message'       => $messages[$feature] ?? "Upgrade to {$required_tier} to unlock this feature.",
            'upgrade_url'   => 'https://castconductor.com/pricing',
        ];
    }
}

/**
 * Global helper function for feature checking
 * 
 * @param string $feature Feature key
 * @return bool
 */
function cc_has_analytics_feature($feature) {
    return CC_Analytics_Helpers::has_feature($feature);
}

/**
 * Global helper function for getting analytics key
 * 
 * @return string
 */
function cc_get_analytics_key() {
    return CC_Analytics_Helpers::get_analytics_key();
}
