<?php

namespace FleekDash\Services\SiteGrowthMeter;

use FleekDash\Models\SiteGrowthEvent;

/**
 * Event Tracker
 *
 * Handles all event tracking for Site Growth Meter
 *
 * @package FleekDash\Services\SiteGrowthMeter
 */
class EventTracker
{
    /**
     * Setup automatic tracking hooks
     *
     * @return void
     */
    public function setupAutomaticTracking()
    {
        // Use both hooks to catch all post publications
        // wp_insert_post fires when post is created/updated
        \add_action('wp_insert_post', array($this, 'trackNewPost'), 10, 2);
        // transition_post_status fires when post status changes (draft -> publish, etc)
        \add_action('transition_post_status', array($this, 'onPostStatusTransition'), 10, 3);
        \add_action('wp_insert_comment', array($this, 'trackNewComment'), 10, 2);
        \add_action('transition_comment_status', array($this, 'onCommentStatusTransition'), 10, 3);
        \add_action('user_register', array($this, 'trackNewUser'), 10, 1);

        if (class_exists('WooCommerce')) {
            \add_action('woocommerce_new_order', array($this, 'trackNewOrder'), 10, 1);
            \add_action('woocommerce_product_set_status_publish', array($this, 'trackNewProduct'), 10, 1);
            \add_action('comment_post', array($this, 'trackProductReview'), 10, 3);
        }

        \add_action('fleekdash_track_site_event', array($this, 'trackManualEvent'), 10, 2);
    }

    /**
     * Track new post
     *
     * @param int $post_id Post ID
     * @param object $post Post object
     */
    public function trackNewPost($post_id, $post)
    {
        // Skip if post is being deleted or is a revision
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }

        // Exclude technical post types (elementor_library, attachment, etc)
        $excluded_types = ['page', 'profile', 'elementor_library', 'attachment', 'e-floating-buttons'];
        $post_types = \get_post_types(['public' => true], 'names');
        $allowed_post_types = array_values(array_diff($post_types, $excluded_types));

        if ($post->post_status === 'publish' && in_array($post->post_type, $allowed_post_types)) {
            $this->incrementEventOccurrence('new_post');
        }
    }

    /**
     * Handle post status transitions (draft -> publish, etc)
     *
     * Added to catch posts that are published after creation
     *
     * @param string $new_status New status
     * @param string $old_status Old status
     * @param object $post Post object
     */
    public function onPostStatusTransition($new_status, $old_status, $post)
    {
        // Only track when transitioning TO publish status
        if ($new_status !== 'publish' || $old_status === 'publish') {
            return;
        }

        // Skip revisions and autosaves
        if (wp_is_post_revision($post->ID) || wp_is_post_autosave($post->ID)) {
            return;
        }

        // Exclude technical post types (elementor_library, attachment, etc)
        $excluded_types = ['page', 'profile', 'elementor_library', 'attachment', 'e-floating-buttons'];
        $post_types = \get_post_types(['public' => true], 'names');
        $allowed_post_types = array_values(array_diff($post_types, $excluded_types));

        if (in_array($post->post_type, $allowed_post_types)) {
            $this->incrementEventOccurrence('new_post');
        }
    }

    /**
     * Normalize wp_insert_comment payload (array or WP_Comment).
     *
     * @param mixed $commentdata Comment data (array or WP_Comment)
     * @return array{approved: string, post_id: int}
     */
    private function parseCommentInsertPayload($commentdata)
    {
        if (!is_object($commentdata)) {
            $approved = isset($commentdata['comment_approved']) ? (string) $commentdata['comment_approved'] : '0';
            $post_id = isset($commentdata['comment_post_ID']) ? (int) $commentdata['comment_post_ID'] : 0;

            return ['approved' => $approved, 'post_id' => $post_id];
        }

        if (method_exists($commentdata, 'get_comment_approved')) {
            return [
                'approved' => (string) $commentdata->get_comment_approved(),
                'post_id' => (int) $commentdata->get_comment_post_ID(),
            ];
        }

        $approved = isset($commentdata->comment_approved) ? (string) $commentdata->comment_approved : '0';
        $post_id = isset($commentdata->comment_post_ID) ? (int) $commentdata->comment_post_ID : 0;

        return ['approved' => $approved, 'post_id' => $post_id];
    }

    /**
     * Track new comment
     *
     * @param int $comment_id Comment ID
     * @param mixed $commentdata Comment data (array or WP_Comment)
     */
    public function trackNewComment($comment_id, $commentdata)
    {
        $parsed = $this->parseCommentInsertPayload($commentdata);

        if ($parsed['post_id'] !== 0) {
            $post_type = function_exists('get_post_type')
                ? call_user_func('get_post_type', $parsed['post_id'])
                : null;
            if ($post_type === 'product') {
                return;
            }
        }

        if ($parsed['approved'] === '1') {
            $this->incrementEventOccurrence('new_comment', ['comment_id' => (int) $comment_id]);
        }
    }

    /**
     * Handle comment status transitions
     *
     * @param string $new_status New status
     * @param string $old_status Old status
     * @param object $comment Comment object
     */
    public function onCommentStatusTransition($new_status, $old_status, $comment)
    {
        $should_track = $new_status === 'approved'
            && $old_status !== 'approved'
            && is_object($comment)
            && property_exists($comment, 'comment_post_ID');

        if (!$should_track) {
            return;
        }

        $post_id = (int) $comment->comment_post_ID;
        $post_type = function_exists('get_post_type') ? call_user_func('get_post_type', $post_id) : null;
        if ($post_id !== 0 && $post_type !== 'product') {
            $this->incrementEventOccurrence('new_comment');
        }
    }

    /**
     * Track new user
     *
     * @param int $user_id User ID
     */
    public function trackNewUser($user_id)
    {
        $this->incrementEventOccurrence('new_user', ['registered_user_id' => (int) $user_id]);
    }

    /**
     * Track new order (WooCommerce)
     *
     * @param int $order_id Order ID
     */
    public function trackNewOrder($order_id)
    {
        $this->incrementEventOccurrence('new_order', ['order_id' => (int) $order_id]);
    }

    /**
     * Track new product (WooCommerce)
     *
     * @param int $product_id Product ID
     */
    public function trackNewProduct($product_id)
    {
        $this->incrementEventOccurrence('new_product', ['product_id' => (int) $product_id]);
    }

    /**
     * Track product review
     *
     * @param int $comment_id Comment ID
     * @param string $comment_approved Approval status
     * @param mixed $commentdata Comment data
     */
    public function trackProductReview($comment_id, $comment_approved, $commentdata)
    {
        $post_id = null;
        if (is_object($commentdata)) {
            if (method_exists($commentdata, 'get_comment_post_ID')) {
                $post_id = $commentdata->get_comment_post_ID();
            } else {
                $post_id = isset($commentdata->comment_post_ID) ? $commentdata->comment_post_ID : null;
            }
        } elseif (is_array($commentdata) && isset($commentdata['comment_post_ID'])) {
            $post_id = $commentdata['comment_post_ID'];
        }

        if ($comment_approved === '1' && $post_id) {
            $post = \get_post($post_id);
            if ($post && $post->post_type === 'product') {
                $this->incrementEventOccurrence('product_review', ['comment_id' => (int) $comment_id]);
            }
        }
    }

    /**
     * Track manual event
     *
     * @param string $event_type Event type
     * @param array $data Event data
     */
    public function trackManualEvent($event_type, $data = [])
    {
        $this->incrementEventOccurrence($event_type, $data);
    }

    /**
     * Increment event occurrence
     *
* UPDATED: Now also records to tracking table for individual date tracking
* SAFETY NET: Auto-creates core events if they don't exist (fallback)
     * Gracefully handles missing tables during plugin activation
     *
     * @param string $event_type Event type
     * @param array $data Additional data
     */
    private function incrementEventOccurrence($event_type, $data = [])
    {
        global $wpdb;

        // Check if table exists before any operation
        // Prevents "Table doesn't exist" error during plugin activation
        if (!SiteGrowthEvent::tableExists()) {
            $this->logIfAvailable(
                'debug',
                'EventTracker: Skipping increment - table does not exist yet (activation in progress)',
            );
            return;
        }

        // getByType() returns first active event, which is correct
        // With unique constraint, only one active event per type will exist
        $event = SiteGrowthEvent::getByType($event_type);

        // SAFETY NET: Auto-create core events if they don't exist
        // This handles edge cases where initialization failed or was skipped
        if (!$event && in_array($event_type, ['new_post', 'new_user', 'new_comment'])) {
            $this->autoCreateCoreEvent($event_type);
            $event = SiteGrowthEvent::getByType($event_type);
        }

        if ($event) {
            // Increment total occurrences in events table
            SiteGrowthEvent::incrementOccurrence($event['id']);

            // NEW: Record individual tracking entry
            $this->recordTrackingEntry($event, $data);

            // Update related goals would be called here
            // Moved to main service to avoid circular dependencies
            \do_action('fleekdash_site_growth_event_tracked', $event_type, $event['id'], $data);
        } else {
            $this->logIfAvailable(
                'debug',
                "EventTracker: Event type '{$event_type}' not found or not active",
            );
        }
    }

    /**
     * Debug helpers may be unavailable during early bootstrap / activation.
     *
     * @param 'debug'|'info'|'error' $level
     */
    private function logIfAvailable(string $level, string $message): void
    {
        $function = match ($level) {
            'debug' => 'fleekdash_log_debug',
            'info' => 'fleekdash_log_info',
            'error' => 'fleekdash_log_error',
            default => null,
        };

        if ($function !== null && \function_exists($function)) {
            $function($message);
        }
    }

    /**
     * Reactivate an existing growth event row (same unique event_type) with canonical core defaults.
     *
     * @param array $config Keys: event_name, description, points_per_event (from core event definitions).
     */
    private function applyCoreEventRowActivation(int $eventId, array $config): void
    {
        SiteGrowthEvent::update($eventId, [
            'event_name' => $config['event_name'],
            'description' => $config['description'],
            'tracking_method' => 'automatic',
            'points_per_event' => $config['points_per_event'],
            'is_active' => 1,
        ]);
    }

    /**
     * @return array{event_name: string, description: string, points_per_event: int}|null
     */
    private function getCoreEventDefinitionOrNull(string $event_type)
    {
        $core_events = [
            'new_post' => [
                'event_name' => 'New Post Published',
                'description' => 'Automatically tracks when new posts are published',
                'points_per_event' => 10,
            ],
            'new_user' => [
                'event_name' => 'New User Registration',
                'description' => 'Automatically tracks when new users register',
                'points_per_event' => 20,
            ],
            'new_comment' => [
                'event_name' => 'New Comment',
                'description' => 'Automatically tracks when new comments are posted',
                'points_per_event' => 5,
            ],
        ];

        return $core_events[$event_type] ?? null;
    }

    /**
     * Row exists for event_type: ensure active; always considered resolved for auto-create flow.
     *
     * @param array<string, mixed> $rowAny
     * @param array{event_name: string, description: string, points_per_event: int} $config
     */
    private function activateExistingCoreEventRow(string $event_type, array $rowAny, array $config): bool
    {
        $is_active = isset($rowAny['is_active']) && (int) $rowAny['is_active'] === 1;
        if (!$is_active && isset($rowAny['id'])) {
            $this->applyCoreEventRowActivation((int) $rowAny['id'], $config);
            $this->logIfAvailable(
                'info',
                "EventTracker: Reactivated core event '{$event_type}' (ID: {$rowAny['id']})",
            );
        }

        return true;
    }

    /**
     * INSERT returned false — recover row written by concurrent request (duplicate key).
     *
     * @param array{event_name: string, description: string, points_per_event: int} $config
     */
    private function recoverCoreEventAfterFailedCreate(string $event_type, array $config): bool
    {
        $after_fail = SiteGrowthEvent::findByEventTypeAnyStatus($event_type);
        if ($after_fail === null || !isset($after_fail['id'])) {
            return false;
        }

        $is_active_after = isset($after_fail['is_active']) && (int) $after_fail['is_active'] === 1;
        if (!$is_active_after) {
            $this->applyCoreEventRowActivation((int) $after_fail['id'], $config);
        }

        $this->logIfAvailable(
            'debug',
            "EventTracker: Resolved core event '{$event_type}' after insert contention (ID: {$after_fail['id']})",
        );

        return true;
    }

    /**
     * @param array{event_name: string, description: string, points_per_event: int} $config
     */
    private function performCoreEventUpsert(string $event_type, array $config): bool
    {
        $existing_active = SiteGrowthEvent::getByType($event_type);
        if ($existing_active !== null) {
            $this->logIfAvailable(
                'debug',
                "EventTracker: Event '{$event_type}' already exists, skipping auto-create",
            );

            return false;
        }

        $row_any = SiteGrowthEvent::findByEventTypeAnyStatus($event_type);
        $upsert_result = false;

        if ($row_any !== null && isset($row_any['id'])) {
            $upsert_result = $this->activateExistingCoreEventRow($event_type, $row_any, $config);
        } else {
            $event_id = SiteGrowthEvent::create([
                'event_type' => $event_type,
                'event_name' => $config['event_name'],
                'description' => $config['description'],
                'tracking_method' => 'automatic',
                'points_per_event' => $config['points_per_event'],
                'is_active' => 1,
                'total_occurrences' => 0,
            ]);

            if ($event_id) {
                $this->logIfAvailable(
                    'info',
                    "EventTracker: Auto-created core event '{$event_type}' (ID: {$event_id})",
                );
                $upsert_result = true;
            } else {
                $upsert_result = $this->recoverCoreEventAfterFailedCreate($event_type, $config);
            }
        }

        return $upsert_result;
    }

    /**
     * Auto-create missing core events (safety net)
     *
     * Creates core events on-demand if they don't exist.
     * This is a fallback for cases where initialization failed.
     *
     * Checks table existence before creating
     *
     * @param string $event_type Event type to create
     * @return bool Success status
     */
    private function autoCreateCoreEvent(string $event_type)
    {
        $config = null;
        if (!SiteGrowthEvent::tableExists()) {
            $this->logIfAvailable(
                'debug',
                "EventTracker: Cannot auto-create '{$event_type}' - table does not exist yet",
            );
        } else {
            $config = $this->getCoreEventDefinitionOrNull($event_type);
        }

        if ($config === null) {
            return false;
        }

        try {
            return $this->performCoreEventUpsert($event_type, $config);
        } catch (\Exception $e) {
            $this->logIfAvailable(
                'error',
                'EventTracker: Failed to auto-create core event \'' . $event_type . '\': ' . $e->getMessage(),
            );

            return false;
        }
    }

    /**
     * Record individual tracking entry
     *
* NEW: Records each event occurrence to tracking table
     * Enables daily/weekly analytics and audit trail
     *
     * @param array $event Event data
     * @param array $data Additional metadata
     */
    private function recordTrackingEntry($event, $data = [])
    {
        global $wpdb;
        $tracking_table = $wpdb->prefix . 'fleekdash_site_growth_tracking';

        // Check if table exists before inserting
        $table_exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s",
            DB_NAME,
            $tracking_table
        ));

        if (!$table_exists) {
            // Table doesn't exist yet, skip tracking (migration not run)
            return;
        }

        $points_earned = $event['points_per_event'] ?? 10;
        $user_id = isset($data['recorded_by']) ? $data['recorded_by'] : \get_current_user_id();

        // If no user (system event), set to null
        if ($user_id === 0) {
            $user_id = null;
        }

        $metadata = !empty($data) ? \wp_json_encode($data) : null;

        $wpdb->insert(
            $tracking_table,
            [
                'event_id' => $event['id'],
                'user_id' => $user_id,
                'points_earned' => $points_earned,
                'tracked_at' => \current_time('mysql'),
                'metadata' => $metadata,
                'created_at' => \current_time('mysql'),
                'updated_at' => \current_time('mysql')
            ],
            ['%d', '%d', '%d', '%s', '%s', '%s', '%s']
        );
    }
}
