<?php
/**
 * Licensed Product Type
 *
 * @package Jeremias\WcLicensedProduct\Product
 */

declare(strict_types=1);

namespace Jeremias\WcLicensedProduct\Product;

use Jeremias\WcLicensedProduct\Admin\SettingsController;

/**
 * Registers and handles the Licensed product types for WooCommerce
 * Supports both simple licensed products and variable licensed products
 */
final class LicensedProductType
{
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->registerHooks();
    }

    /**
     * Register WordPress hooks
     */
    private function registerHooks(): void
    {
        // Ensure product type terms exist in taxonomy (for WC_Product_Factory::get_product_type())
        add_action('woocommerce_init', [$this, 'ensureProductTypeTermsExist']);

        // Register product types
        add_filter('product_type_selector', [$this, 'addProductType']);
        add_filter('woocommerce_product_class', [$this, 'getProductClass'], 10, 4);

        // Add product data tabs
        add_filter('woocommerce_product_data_tabs', [$this, 'addProductDataTab']);
        add_action('woocommerce_product_data_panels', [$this, 'addProductDataPanel']);

        // Save product meta
        add_action('woocommerce_process_product_meta_licensed', [$this, 'saveProductMeta']);
        add_action('woocommerce_process_product_meta_licensed-variable', [$this, 'saveProductMeta']);

        // Show price and add to cart for licensed products
        add_action('woocommerce_licensed_add_to_cart', [$this, 'addToCartTemplate']);
        add_action('woocommerce_licensed-variable_add_to_cart', [$this, 'variableAddToCartTemplate']);

        // Use variable product add-to-cart handler for licensed-variable products
        add_filter('woocommerce_add_to_cart_handler', [$this, 'addToCartHandler'], 10, 2);

        // Make product virtual by default
        add_filter('woocommerce_product_is_virtual', [$this, 'isVirtual'], 10, 2);

        // Hide stock HTML for licensed products
        add_filter('woocommerce_get_stock_html', [$this, 'hideStockHtml'], 10, 2);
        add_filter('woocommerce_get_availability', [$this, 'hideAvailability'], 10, 2);
        add_filter('woocommerce_get_availability_text', [$this, 'hideAvailabilityText'], 10, 2);
        add_filter('woocommerce_product_get_stock_quantity', [$this, 'hideStockQuantity'], 10, 2);
        add_filter('woocommerce_product_variation_get_stock_quantity', [$this, 'hideStockQuantity'], 10, 2);

        // Display current version under product title on single product page
        add_action('woocommerce_single_product_summary', [$this, 'displayCurrentVersion'], 6);

        // Enqueue frontend CSS for licensed products on single product pages
        add_action('wp_enqueue_scripts', [$this, 'enqueueFrontendStyles']);

        // Variable product support - variation settings
        add_action('woocommerce_variation_options', [$this, 'addVariationOptions'], 10, 3);
        add_action('woocommerce_product_after_variable_attributes', [$this, 'addVariationFields'], 10, 3);
        add_action('woocommerce_save_product_variation', [$this, 'saveVariationFields'], 10, 2);

        // Admin scripts for licensed-variable type
        add_action('admin_footer', [$this, 'addVariableProductScripts']);
    }

    /**
     * Ensure product type terms exist in the product_type taxonomy
     * This is required for WC_Product_Factory::get_product_type() to work correctly
     */
    public function ensureProductTypeTermsExist(): void
    {
        \Jeremias\WcLicensedProduct\Installer::registerProductTypes();
    }

    /**
     * Add product types to selector
     */
    public function addProductType(array $types): array
    {
        $types['licensed'] = __('Licensed Product', 'wc-licensed-product');
        $types['licensed-variable'] = __('Licensed Variable Product', 'wc-licensed-product');
        return $types;
    }

    /**
     * Get product class for licensed types
     *
     * @param string $className Default class name
     * @param string $productType Product type
     * @param string $postType Post type (usually 'product' or 'product_variation')
     * @param mixed  $productId Product ID (can be int or string)
     */
    public function getProductClass(string $className, string $productType, string $postType = '', $productId = 0): string
    {
        if ($productType === 'licensed') {
            return LicensedProduct::class;
        }
        if ($productType === 'licensed-variable') {
            return LicensedVariableProduct::class;
        }
        // Handle variations of licensed-variable products
        // Check both by product type and by post type for variations
        if ($productType === 'variation' || $postType === 'product_variation') {
            // Get parent ID from the product post
            $parentId = 0;
            $productIdInt = (int) $productId;
            if ($productIdInt > 0) {
                $parentId = wp_get_post_parent_id($productIdInt);
            }
            // Fallback to global $post if product ID not available
            if (!$parentId) {
                global $post;
                if ($post && $post->post_parent) {
                    $parentId = (int) $post->post_parent;
                }
            }

            if ($parentId) {
                $parentType = \WC_Product_Factory::get_product_type($parentId);
                if ($parentType === 'licensed-variable') {
                    return LicensedProductVariation::class;
                }
            }
        }
        return $className;
    }

    /**
     * Add product data tab for license settings
     * Also modify variations tab to show for licensed-variable products
     */
    public function addProductDataTab(array $tabs): array
    {
        // Add our License Settings tab
        $tabs['licensed_product'] = [
            'label' => __('License Settings', 'wc-licensed-product'),
            'target' => 'licensed_product_data',
            'class' => ['show_if_licensed', 'show_if_licensed-variable'],
            'priority' => 21,
        ];

        // Make Variations tab also show for licensed-variable products
        if (isset($tabs['variations'])) {
            $tabs['variations']['class'][] = 'show_if_licensed-variable';
        }

        return $tabs;
    }

    /**
     * Add product data panel content
     */
    public function addProductDataPanel(): void
    {
        global $post;

        // Get current product values
        $currentMaxActivations = get_post_meta($post->ID, '_licensed_max_activations', true);
        $currentValidityDays = get_post_meta($post->ID, '_licensed_validity_days', true);
        $currentBindToVersion = get_post_meta($post->ID, '_licensed_bind_to_version', true);

        // Get default values
        $defaultMaxActivations = SettingsController::getDefaultMaxActivations();
        $defaultValidityDays = SettingsController::getDefaultValidityDays();
        $defaultBindToVersion = SettingsController::getDefaultBindToVersion();

        // Format default validity for display
        $defaultValidityDisplay = $defaultValidityDays !== null
            ? sprintf(__('%d days', 'wc-licensed-product'), $defaultValidityDays)
            : __('Lifetime', 'wc-licensed-product');

        ?>
        <div id="licensed_product_data" class="panel woocommerce_options_panel hidden">
            <div class="options_group">
                <p class="form-field">
                    <em><?php
                        printf(
                            /* translators: %s: URL to settings page */
                            esc_html__('Leave fields empty to use default settings from %s.', 'wc-licensed-product'),
                            '<a href="' . esc_url(admin_url('admin.php?page=wc-settings&tab=licensed_product')) . '">' .
                            esc_html__('WooCommerce > Settings > Licensed Products', 'wc-licensed-product') . '</a>'
                        );
                    ?></em>
                </p>
                <?php
                woocommerce_wp_text_input([
                    'id' => '_licensed_max_activations',
                    'label' => __('Max Activations', 'wc-licensed-product'),
                    'description' => sprintf(
                        /* translators: %d: default max activations value */
                        __('Maximum number of domain activations per license. Default: %d', 'wc-licensed-product'),
                        $defaultMaxActivations
                    ),
                    'desc_tip' => true,
                    'type' => 'number',
                    'custom_attributes' => [
                        'min' => '1',
                        'step' => '1',
                    ],
                    'placeholder' => (string) $defaultMaxActivations,
                    'value' => $currentMaxActivations,
                ]);

                woocommerce_wp_text_input([
                    'id' => '_licensed_validity_days',
                    'label' => __('License Validity (Days)', 'wc-licensed-product'),
                    'description' => sprintf(
                        /* translators: %s: default validity value */
                        __('Number of days the license is valid. Leave empty for default (%s).', 'wc-licensed-product'),
                        $defaultValidityDisplay
                    ),
                    'desc_tip' => true,
                    'type' => 'number',
                    'custom_attributes' => [
                        'min' => '0',
                        'step' => '1',
                    ],
                    'placeholder' => $defaultValidityDays !== null ? (string) $defaultValidityDays : __('Lifetime', 'wc-licensed-product'),
                    'value' => $currentValidityDays,
                ]);

                woocommerce_wp_checkbox([
                    'id' => '_licensed_bind_to_version',
                    'label' => __('Bind to Major Version', 'wc-licensed-product'),
                    'description' => sprintf(
                        /* translators: %s: default bind to version value (Yes/No) */
                        __('If enabled, licenses are bound to the major version at purchase time. Default: %s', 'wc-licensed-product'),
                        $defaultBindToVersion ? __('Yes', 'wc-licensed-product') : __('No', 'wc-licensed-product')
                    ),
                    'value' => $currentBindToVersion ?: ($defaultBindToVersion ? 'yes' : 'no'),
                    'cbvalue' => 'yes',
                ]);
                ?>
            </div>
        </div>
        <?php
    }

    /**
     * Save product meta
     */
    public function saveProductMeta(int $postId): void
    {
        // Verify nonce is handled by WooCommerce
        // Allow empty values to fall back to defaults
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by WooCommerce
        $maxActivations = isset($_POST['_licensed_max_activations']) && $_POST['_licensed_max_activations'] !== ''
            ? absint($_POST['_licensed_max_activations'])
            : '';
        update_post_meta($postId, '_licensed_max_activations', $maxActivations);

        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        $validityDays = isset($_POST['_licensed_validity_days']) && $_POST['_licensed_validity_days'] !== ''
            ? absint($_POST['_licensed_validity_days'])
            : '';
        update_post_meta($postId, '_licensed_validity_days', $validityDays);

        // For checkbox, we need to distinguish between "not set" and "explicitly unchecked"
        // If the hidden field is present, the form was submitted and we save the actual value
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        $bindToVersion = isset($_POST['_licensed_bind_to_version']) ? 'yes' : 'no';
        update_post_meta($postId, '_licensed_bind_to_version', $bindToVersion);
    }

    /**
     * Add to cart template for licensed products
     */
    public function addToCartTemplate(): void
    {
        wc_get_template('single-product/add-to-cart/simple.php');
    }

    /**
     * Use the variable product add-to-cart handler for licensed-variable products
     * WooCommerce uses product type to determine which handler to use
     */
    public function addToCartHandler(string $handler, \WC_Product $product): string
    {
        if ($product->is_type('licensed-variable')) {
            return 'variable';
        }
        return $handler;
    }

    /**
     * Hide stock HTML for licensed products (they're always virtual/in-stock)
     */
    public function hideStockHtml(string $html, \WC_Product $product): string
    {
        if ($this->isLicensedProductOrVariation($product)) {
            return '';
        }
        return $html;
    }

    /**
     * Hide availability data for licensed products (they're always virtual/in-stock)
     */
    public function hideAvailability(array $availability, \WC_Product $product): array
    {
        if ($this->isLicensedProductOrVariation($product)) {
            return [
                'availability' => '',
                'class'        => '',
            ];
        }
        return $availability;
    }

    /**
     * Hide availability text for licensed products
     */
    public function hideAvailabilityText(string $availability, \WC_Product $product): string
    {
        if ($this->isLicensedProductOrVariation($product)) {
            return '';
        }
        return $availability;
    }

    /**
     * Hide stock quantity for licensed products (return null = no stock display)
     *
     * @param int|null $quantity
     * @param \WC_Product $product
     * @return int|null
     */
    public function hideStockQuantity($quantity, \WC_Product $product)
    {
        if ($this->isLicensedProductOrVariation($product)) {
            return null;
        }
        return $quantity;
    }

    /**
     * Check if product is a licensed product or variation of one
     */
    private function isLicensedProductOrVariation(\WC_Product $product): bool
    {
        // Direct licensed products
        if ($product->is_type('licensed') || $product->is_type('licensed-variable')) {
            return true;
        }

        // Check by class name for our custom variation class
        if ($product instanceof LicensedProductVariation) {
            return true;
        }

        // Check if this is a variation with a licensed-variable parent
        // Use WC_Product_Factory::get_product_type() to get parent type directly from DB
        // This is more reliable than loading the full product object
        $parentId = $product->get_parent_id();
        if ($parentId) {
            $parentType = \WC_Product_Factory::get_product_type($parentId);
            if ($parentType === 'licensed-variable') {
                return true;
            }
        }

        return false;
    }

    /**
     * Make licensed products virtual by default
     */
    public function isVirtual(bool $isVirtual, \WC_Product $product): bool
    {
        if ($this->isLicensedProductOrVariation($product)) {
            return true;
        }
        return $isVirtual;
    }

    /**
     * Enqueue frontend styles and scripts for licensed products on single product pages
     */
    public function enqueueFrontendStyles(): void
    {
        if (!is_product()) {
            return;
        }

        global $product;

        if (!$product || (!$product->is_type('licensed') && !$product->is_type('licensed-variable'))) {
            return;
        }

        wp_enqueue_style(
            'wc-licensed-product-frontend',
            WC_LICENSED_PRODUCT_PLUGIN_URL . 'assets/css/frontend.css',
            [],
            WC_LICENSED_PRODUCT_VERSION
        );

        // For licensed-variable products, enqueue WooCommerce variation scripts
        if ($product->is_type('licensed-variable')) {
            wp_enqueue_script('wc-add-to-cart-variation');
        }
    }

    /**
     * Display current version under product title on single product page
     */
    public function displayCurrentVersion(): void
    {
        global $product;

        if (!$product || (!$product->is_type('licensed') && !$product->is_type('licensed-variable'))) {
            return;
        }

        /** @var LicensedProduct|LicensedVariableProduct $product */
        $version = $product->get_current_version();

        if (empty($version)) {
            return;
        }

        printf(
            '<p class="wclp-product-version"><span class="version-label">%s</span> <span class="version-number">%s</span></p>',
            esc_html__('Version:', 'wc-licensed-product'),
            esc_html($version)
        );
    }

    /**
     * Add to cart template for variable licensed products
     * This mirrors WooCommerce's woocommerce_variable_add_to_cart() function
     */
    public function variableAddToCartTemplate(): void
    {
        global $product;

        // The hook woocommerce_licensed-variable_add_to_cart only fires for this product type
        // so we just need to verify the product exists
        if (!$product) {
            return;
        }

        // Ensure we're working with a product that has variable product methods
        // Re-load the product to ensure we get the correct class instance
        $productId = $product->get_id();
        $variableProduct = wc_get_product($productId);

        if (!$variableProduct || !method_exists($variableProduct, 'get_variation_attributes')) {
            // Fallback to simple add to cart if not a variable product
            wc_get_template('single-product/add-to-cart/simple.php');
            return;
        }

        // Update global $product to use the correctly loaded instance
        // This ensures the template has the right product type
        $product = $variableProduct;

        // Get variations count to determine if we should load them via AJAX
        $children = $variableProduct->get_children();
        $getVariations = count($children) <= apply_filters('woocommerce_ajax_variation_threshold', 30, $variableProduct);

        // Get template variables - WooCommerce expects these to be set
        $availableVariations = $getVariations ? $variableProduct->get_available_variations() : false;
        $attributes = $variableProduct->get_variation_attributes();
        $selectedAttributes = $variableProduct->get_default_attributes();

        // Ensure arrays (WooCommerce template expects arrays, not null)
        if (!is_array($attributes)) {
            $attributes = [];
        }
        if (!is_array($selectedAttributes)) {
            $selectedAttributes = [];
        }

        wc_get_template(
            'single-product/add-to-cart/variable.php',
            [
                'available_variations' => $availableVariations,
                'attributes'           => $attributes,
                'selected_attributes'  => $selectedAttributes,
            ]
        );
    }

    /**
     * Add variation options (checkboxes next to variation header)
     */
    public function addVariationOptions(int $loop, array $variationData, \WP_Post $variation): void
    {
        // Check if parent is licensed-variable
        $parentId = $variation->post_parent;
        $parentProduct = wc_get_product($parentId);

        if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) {
            return;
        }

        $isVirtual = get_post_meta($variation->ID, '_virtual', true);
        ?>
        <label class="tips" data-tip="<?php esc_attr_e('Licensed products are always virtual', 'wc-licensed-product'); ?>">
            <input type="checkbox" class="checkbox" disabled checked />
            <?php esc_html_e('Virtual', 'wc-licensed-product'); ?>
        </label>
        <?php
    }

    /**
     * Add variation fields for license settings
     */
    public function addVariationFields(int $loop, array $variationData, \WP_Post $variation): void
    {
        // Check if parent is licensed-variable
        $parentId = $variation->post_parent;
        $parentProduct = wc_get_product($parentId);

        if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) {
            return;
        }

        // Get variation values
        $validityDays = get_post_meta($variation->ID, '_licensed_validity_days', true);
        $maxActivations = get_post_meta($variation->ID, '_licensed_max_activations', true);

        // Get parent defaults for placeholder
        $parentValidityDays = $parentProduct->get_validity_days();
        $parentMaxActivations = $parentProduct->get_max_activations();

        $parentValidityDisplay = $parentValidityDays !== null
            ? sprintf(__('%d days', 'wc-licensed-product'), $parentValidityDays)
            : __('Lifetime', 'wc-licensed-product');

        ?>
        <div class="wclp-variation-license-settings">
            <p class="form-row form-row-first">
                <label><?php esc_html_e('License Duration (Days)', 'wc-licensed-product'); ?></label>
                <input type="number"
                       name="wclp_validity_days[<?php echo esc_attr($loop); ?>]"
                       class="short"
                       min="0"
                       step="1"
                       placeholder="<?php echo esc_attr($parentValidityDisplay); ?>"
                       value="<?php echo esc_attr($validityDays); ?>"
                />
                <span class="description"><?php esc_html_e('Leave empty for parent default. 0 = Lifetime.', 'wc-licensed-product'); ?></span>
            </p>
            <p class="form-row form-row-last">
                <label><?php esc_html_e('Max Activations', 'wc-licensed-product'); ?></label>
                <input type="number"
                       name="wclp_max_activations[<?php echo esc_attr($loop); ?>]"
                       class="short"
                       min="1"
                       step="1"
                       placeholder="<?php echo esc_attr($parentMaxActivations); ?>"
                       value="<?php echo esc_attr($maxActivations); ?>"
                />
                <span class="description"><?php esc_html_e('Leave empty for parent default.', 'wc-licensed-product'); ?></span>
            </p>
        </div>
        <style>
            .wclp-variation-license-settings {
                background: #f8f8f8;
                border: 1px solid #e5e5e5;
                padding: 10px;
                margin: 10px 0;
                border-radius: 4px;
            }
            .wclp-variation-license-settings .description {
                display: block;
                font-style: italic;
                color: #666;
                margin-top: 4px;
            }
        </style>
        <?php
    }

    /**
     * Save variation fields
     */
    public function saveVariationFields(int $variationId, int $loop): void
    {
        // Check if parent is licensed-variable
        $variation = wc_get_product($variationId);
        if (!$variation) {
            return;
        }

        $parentProduct = wc_get_product($variation->get_parent_id());
        if (!$parentProduct || !$parentProduct->is_type('licensed-variable')) {
            return;
        }

        // Save validity days
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is verified by WooCommerce
        if (isset($_POST['wclp_validity_days'][$loop])) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing
            $validityDays = sanitize_text_field($_POST['wclp_validity_days'][$loop]);
            if ($validityDays !== '') {
                update_post_meta($variationId, '_licensed_validity_days', absint($validityDays));
            } else {
                delete_post_meta($variationId, '_licensed_validity_days');
            }
        }

        // Save max activations
        // phpcs:ignore WordPress.Security.NonceVerification.Missing
        if (isset($_POST['wclp_max_activations'][$loop])) {
            // phpcs:ignore WordPress.Security.NonceVerification.Missing
            $maxActivations = sanitize_text_field($_POST['wclp_max_activations'][$loop]);
            if ($maxActivations !== '') {
                update_post_meta($variationId, '_licensed_max_activations', absint($maxActivations));
            } else {
                delete_post_meta($variationId, '_licensed_max_activations');
            }
        }

        // Set variation as virtual (licensed products are always virtual)
        update_post_meta($variationId, '_virtual', 'yes');
    }

    /**
     * Add JavaScript for licensed product types in admin
     * Handles visibility of License Settings tab and Product Versions meta box
     */
    public function addVariableProductScripts(): void
    {
        global $post, $pagenow;

        if ($pagenow !== 'post.php' && $pagenow !== 'post-new.php') {
            return;
        }

        if (!$post || get_post_type($post) !== 'product') {
            return;
        }

        ?>
        <script type="text/javascript">
            jQuery(document).ready(function($) {
                // Handle our custom License Settings tab, Product Versions meta box,
                // and show_if_licensed-variable elements
                function toggleOurElements() {
                    var productType = $('#product-type').val();
                    var isLicensed = productType === 'licensed';
                    var isLicensedVariable = productType === 'licensed-variable';

                    // License Settings tab - use CSS class for visibility
                    var $licenseTab = $('li.licensed_product_options');
                    if (isLicensed || isLicensedVariable) {
                        $licenseTab.addClass('wclp-active');
                    } else {
                        $licenseTab.removeClass('wclp-active');
                        // If License Settings panel is active, switch to General tab
                        if ($('#licensed_product_data').is(':visible')) {
                            $('li.general_options a').trigger('click');
                        }
                    }

                    // Product Versions meta box
                    var $metaBox = $('#wc_licensed_product_versions');
                    if (isLicensed || isLicensedVariable) {
                        $metaBox.css('display', '');
                    } else {
                        $metaBox.css('display', 'none');
                    }

                    // Handle show_if_licensed-variable elements (like Variations tab)
                    // WooCommerce doesn't know about our custom product types
                    if (isLicensedVariable) {
                        $('.show_if_licensed-variable').show();
                        // Also show elements that should be visible for variable products
                        // since licensed-variable is a variable product type
                        $('.show_if_variable').show();
                        $('.hide_if_variable').hide();
                    } else {
                        // Let WooCommerce handle show_if_variable elements
                        // We only need to hide our custom class when not licensed-variable
                        // Don't hide show_if_licensed-variable when it's licensed (simple)
                        if (!isLicensed) {
                            $('.show_if_licensed-variable').not('.show_if_licensed').hide();
                        }
                    }
                }

                // Initial setup - run after WooCommerce has initialized
                setTimeout(toggleOurElements, 10);

                // On product type change - run after WooCommerce has processed
                $('#product-type').on('change', function() {
                    setTimeout(toggleOurElements, 100);
                });

                // Re-apply after WooCommerce AJAX operations
                $(document).on('woocommerce_variations_loaded woocommerce_variations_added woocommerce_variations_saved', function() {
                    setTimeout(toggleOurElements, 10);
                });
            });
        </script>
        <?php
    }
}
