<?php

namespace RecastPaywall\Sync;

use RecastPaywall\Api\RecastClient;

/**
 * Inventory synchronization service
 */
class InventorySync {
    /**
     * Recast client
     */
    private RecastClient $recastClient;

    /**
     * Constructor
     */
    public function __construct(RecastClient $recastClient) {
        $this->recastClient = $recastClient;
    }

    /**
     * Get brand ID from settings
     */
    private function getBrandId(): string {
        $settings = get_option('recast_paywall_settings', []);
        return trim($settings['brand_id'] ?? '');
    }

    /**
     * Get post statuses that are eligible for syncing
     * 
     * @return array<string>
     */
    public function getSyncableStatuses(): array {
        return ['draft', 'future', 'pending', 'private', 'publish'];
    }

    /**
     * Check if a post status is syncable
     */
    public function isStatusSyncable(string $status): bool {
        return in_array($status, $this->getSyncableStatuses(), true);
    }

    /**
     * Sync post with Recast inventory
     */
    public function syncPost(int $postId, \WP_Post $post): void {
        // If this is a REST request, defer to rest_after_insert_post handler to ensure fresh meta values
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return;
        }
        // Skip auto-saves and revisions
        if (wp_is_post_revision($postId) || wp_is_post_autosave($postId)) {
            return;
        }

        // Only sync posts in editable statuses (allow draft, future, pending, private, publish)
        // Skip trash, auto-draft, and inherit
        if (!$this->isStatusSyncable($post->post_status)) {
            return;
        }

        // Check if post should be synced
        if (!$this->shouldSyncPost($postId)) {
            return;
        }

        $this->performSync($postId, $post);
    }

    /**
     * Sync after REST API inserts/updates a post (Gutenberg save flow)
     */
    public function handleRestAfterInsert(\WP_Post $post, \WP_REST_Request $request, bool $creating): void {
        // Only sync posts in editable statuses (allow draft, future, pending, private, publish)
        // Skip trash, auto-draft, and inherit
        if (!$this->isStatusSyncable($post->post_status)) {
            return;
        }

        // Check if post should be synced
        if (!$this->shouldSyncPost($post->ID)) {
            return;
        }

        // Try immediate sync first, then fallback to scheduled if needed
        $result = $this->performSync($post->ID, $post);
        
        if (!$result) {
            // If immediate sync failed, schedule a retry
            wp_schedule_single_event(time() + 5, 'recast_paywall_delayed_sync', [$post->ID]);
        }
    }

    /**
     * Handle post status transition
     */
    public function handleStatusTransition(string $newStatus, string $oldStatus, \WP_Post $post): void {
        // If transitioning TO a syncable status, sync the post
        if ($this->isStatusSyncable($newStatus) && $oldStatus !== $newStatus) {
            $this->syncPost($post->ID, $post);
        }
        
        // If transitioning to trash, handle deletion
        if ($newStatus === 'trash' && $oldStatus !== 'trash') {
            $this->handlePostDeletion($post->ID);
        }
    }

    /**
     * Handle post deletion
     */
    public function handlePostDeletion(int $postId): void {
        $settings = get_option('recast_paywall_settings', []);
        
        if (empty($settings['archive_on_delete'])) {
            return;
        }

        $itemId = get_post_meta($postId, '_recast_inventory_item_id', true);
        
        if (empty($itemId)) {
            return;
        }

        $result = $this->recastClient->archiveInventoryItem($itemId);
        
        if (is_wp_error($result)) {
            $this->logError('Failed to archive inventory item', [
                'post_id' => $postId,
                'item_id' => $itemId,
                'error' => $result->get_error_message(),
            ]);
        }
    }

    /**
     * Perform the actual sync
     */
    private function performSync(int $postId, \WP_Post $post): bool {
        // Prefix externalId with brandId for proper namespacing
        // Note: Use hyphen separator (not underscore) - API requires /^[0-9a-zA-Z-]+$/
        $brandId = $this->getBrandId();
        $externalId = $brandId ? $brandId . '-' . $postId : (string) $postId;
        
        $data = [
            'externalId' => $externalId,
            'title' => $post->post_title,
            'description' => $this->getPostDescription($post),
        ];

        // get the recast inventory item id
        $itemId = get_post_meta($postId, '_recast_inventory_item_id', true);
        if ($itemId !== '') {
            $data['id'] = $itemId;
        }

        // Optional fields from post meta
        $price = get_post_meta($postId, '_recast_price', true);
        if ($price !== '') {
            $priceValue = (int) $price;
            if ($priceValue > 0) {
                $data['priceDetails'] = [ 'price' => $priceValue ];
            }
        }
        $status = get_post_meta($postId, '_recast_status', true);
        if (!empty($status)) {
            $data['statusDetails'] = [ 'status' => $status ];
        }
        $geo = get_post_meta($postId, '_recast_geo_availability', true);
        if (!empty($geo)) {
            $codes = array_filter(array_map('trim', explode(',', $geo)));
            if (!empty($codes)) {
                $data['geoAvailability'] = $codes;
            }
        }
        $aff = get_post_meta($postId, '_recast_affiliate_share_pct', true);
        if ($aff !== '') {
            $data['affiliateSharePct'] = (float) $aff;
        }

        $result = $this->recastClient->createOrUpdateInventoryItem($data);
        
        if (is_wp_error($result)) {

            /**
             * Action fired when a post sync to Recast fails.
             */
            do_action('recast_paywall_sync_failed', $postId, $result);

            // Schedule retry
            $this->scheduleRetry($postId, $post);
            return false;
        }

        // Store the inventory item ID
        if (!empty($result['id'])) {
            update_post_meta($postId, '_recast_inventory_item_id', $result['id']);
        }

        // Update sync timestamp
        update_post_meta($postId, '_recast_last_synced_at', current_time('mysql'));

        /**
         * Action fired when a post has been successfully synced to Recast.
         */
        do_action('recast_paywall_sync_completed', $postId, $result);
        
        return true;
    }

    /**
     * Force sync a post regardless of paywall/other checks
     */
    public function forceSyncPost(int $postId): bool {
        $post = get_post($postId);
        if (!$post) {
            return false;
        }
        return $this->performSync($postId, $post);
    }

    /**
     * Check if post should be synced
     */
    public function shouldSyncPost(int $postId): bool {
        // Sync if post-level paywall is enabled or gating mode is 'block'
        $isPaywalled = get_post_meta($postId, '_recast_paywall_enabled', true);
        $mode = get_post_meta($postId, '_recast_gating_mode', true) ?: 'post';
        $itemId = get_post_meta($postId, '_recast_inventory_item_id', true);
        
        $shouldSync = !empty($isPaywalled) || $mode === 'block' || !empty($itemId);

        return $shouldSync;
    }

    /**
     * Get post description for Recast
     */
    private function getPostDescription(\WP_Post $post): string {
        // Prefer manual excerpt; otherwise derive from content
        $text = !empty($post->post_excerpt)
            ? wp_strip_all_tags($post->post_excerpt)
            : wp_strip_all_tags($post->post_content);

        // Trim to first 20 words
        return wp_trim_words($text, 20, '...');
    }

    /**
     * Schedule retry for failed sync
     */
    private function scheduleRetry(int $postId, \WP_Post $post): void {
        $retryCount = get_post_meta($postId, '_recast_sync_retry_count', true);
        $retryCount = intval($retryCount) + 1;
        
        if ($retryCount <= 3) {
            update_post_meta($postId, '_recast_sync_retry_count', $retryCount);
            
            // Schedule retry with exponential backoff
            $delay = pow(2, $retryCount) * 60; // 2, 4, 8 minutes
            
            wp_schedule_single_event(
                time() + $delay,
                'recast_paywall_sync_retry',
                [$postId]
            );
        }
    }

    /**
     * Handle AJAX sync request
     */
    public function handleAjaxSync(): void {
        if (!current_user_can('edit_posts')) {
            wp_die(esc_html__('Unauthorized', 'recast-paywall'));
        }

        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')), 'recast_sync_post')) {
            wp_die(esc_html__('Invalid nonce', 'recast-paywall'));
        }

        $postId = intval($_POST['post_id'] ?? 0);
        
        if (!$postId) {
            wp_send_json_error(['message' => __('Invalid post ID', 'recast-paywall')]);
        }

        $post = get_post($postId);
        
        if (!$post) {
            wp_send_json_error(['message' => __('Post not found', 'recast-paywall')]);
        }

        // Clear any existing retry count
        delete_post_meta($postId, '_recast_sync_retry_count');
        
        // Perform sync
        $this->performSync($postId, $post);
        
        wp_send_json_success([
            'message' => __('Post synced successfully', 'recast-paywall'),
            'item_id' => get_post_meta($postId, '_recast_inventory_item_id', true),
            'last_synced' => get_post_meta($postId, '_recast_last_synced_at', true),
        ]);
    }

    /**
     * Handle retry event
     */
    public function handleRetryEvent(int $postId): void {
        $post = get_post($postId);
        
        if (!$post || $post->post_status !== 'publish') {
            return;
        }

        $this->performSync($postId, $post);
    }

    /**
     * Get sync status for a post
     */
    public function getSyncStatus(int $postId): array {
        $itemId = get_post_meta($postId, '_recast_inventory_item_id', true);
        $lastSynced = get_post_meta($postId, '_recast_last_synced_at', true);
        $retryCount = get_post_meta($postId, '_recast_sync_retry_count', true);
        $isPaywalled = get_post_meta($postId, '_recast_paywall_enabled', true);

        return [
            'item_id' => $itemId,
            'last_synced' => $lastSynced,
            'retry_count' => intval($retryCount),
            'is_paywalled' => !empty($isPaywalled),
            'status' => $this->getStatusText($itemId, $retryCount),
        ];
    }

    /**
     * Get status text
     */
    private function getStatusText(?string $itemId, int $retryCount): string {
        if (!empty($itemId)) {
            return __('Synced', 'recast-paywall');
        }

        if ($retryCount > 0) {
            return sprintf(
                // translators: %d is the number of retries attempted for syncing
                __('Sync failed (%d retries)', 'recast-paywall'), 
                $retryCount
            );
        }

        return __('Not synced', 'recast-paywall');
    }

    /**
     * Log error
     */
    private function logError(string $message, array $context = []): void {
        // Debug logging removed for production
    }

    /**
     * Log info messages when debugging is enabled
     */
    private function logInfo(string $message, array $context = []): void {
        // Debug logging removed for production
    }
}
