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

declare(strict_types=1);

namespace Jeremias\WcLicensedProduct\Product;

use Jeremias\WcLicensedProduct\Admin\SettingsController;
use WC_Product_Variable;

/**
 * Licensed Variable Product type extending WooCommerce Variable Product
 *
 * This allows selling license subscriptions with different durations
 * (e.g., monthly, yearly, lifetime) as product variations.
 */
class LicensedVariableProduct extends WC_Product_Variable
{
    /**
     * Product type
     */
    protected $product_type = 'licensed-variable';

    /**
     * Constructor
     */
    public function __construct($product = 0)
    {
        parent::__construct($product);
    }

    /**
     * Get product type
     */
    public function get_type(): string
    {
        return 'licensed-variable';
    }

    /**
     * Check if product is of a certain type
     * Override to return true for 'variable' as well, so WooCommerce internal
     * checks pass (many methods in WC_Product_Variable check is_type('variable'))
     */
    public function is_type($type): bool
    {
        if (is_array($type)) {
            return in_array($this->get_type(), $type, true) || in_array('variable', $type, true);
        }
        return $this->get_type() === $type || 'variable' === $type;
    }

    /**
     * Licensed products are always virtual
     */
    public function is_virtual(): bool
    {
        return true;
    }

    /**
     * Licensed variable products are purchasable if the parent check passes
     * Variable products don't have a direct price - their variations do
     */
    public function is_purchasable(): bool
    {
        // Use the parent WC_Product_Variable logic
        // which checks exists() and status, not price
        return parent::is_purchasable();
    }

    /**
     * Licensed products are always in stock (virtual, no inventory)
     */
    public function is_in_stock(): bool
    {
        return true;
    }

    /**
     * Get children (variations) for this product
     * Override because WC_Product_Variable::get_children() checks is_type('variable')
     * which fails for our 'licensed-variable' type
     */
    public function get_children($context = 'view'): array
    {
        if (!$this->get_id()) {
            return [];
        }

        // Query variations directly from database since WooCommerce's data store
        // doesn't work properly with custom variable product types
        global $wpdb;
        $children = $wpdb->get_col($wpdb->prepare(
            "SELECT ID FROM {$wpdb->posts}
             WHERE post_parent = %d
             AND post_type = 'product_variation'
             AND post_status IN ('publish', 'private')
             ORDER BY menu_order ASC, ID ASC",
            $this->get_id()
        ));

        $children = array_map('intval', $children);

        if ('view' === $context) {
            $children = apply_filters('woocommerce_get_children', $children, $this, false);
        }

        return is_array($children) ? $children : [];
    }

    /**
     * Get variation attributes for this product
     * Override because WC_Product_Variable uses data_store which doesn't work
     * properly with custom variable product types
     */
    public function get_variation_attributes(): array
    {
        $attributes = $this->get_attributes();

        if (!$attributes || !is_array($attributes)) {
            return [];
        }

        $variation_attributes = [];

        foreach ($attributes as $attribute) {
            // For WC_Product_Attribute objects
            if ($attribute instanceof \WC_Product_Attribute) {
                if ($attribute->get_variation()) {
                    $attribute_name = $attribute->get_name();

                    // For taxonomy attributes, get term slugs
                    if ($attribute->is_taxonomy()) {
                        $attribute_terms = wc_get_product_terms(
                            $this->get_id(),
                            $attribute_name,
                            ['fields' => 'slugs']
                        );
                        $variation_attributes[$attribute_name] = $attribute_terms;
                    } else {
                        // For custom attributes, get options directly
                        $variation_attributes[$attribute_name] = $attribute->get_options();
                    }
                }
            }
            // For array-based attributes (older format)
            elseif (is_array($attribute) && !empty($attribute['is_variation'])) {
                $attribute_name = $attribute['name'];
                $values = isset($attribute['value']) ? explode('|', $attribute['value']) : [];
                $variation_attributes[$attribute_name] = array_map('trim', $values);
            }
        }

        return $variation_attributes;
    }

    /**
     * Get variation prices (regular, sale, and final prices)
     * Override because WC_Product_Variable uses data_store which doesn't work
     * properly with custom variable product types
     */
    public function get_variation_prices($for_display = false): array
    {
        $children = $this->get_children();

        if (empty($children)) {
            return [
                'price' => [],
                'regular_price' => [],
                'sale_price' => [],
            ];
        }

        $prices = [
            'price' => [],
            'regular_price' => [],
            'sale_price' => [],
        ];

        foreach ($children as $child_id) {
            $variation = wc_get_product($child_id);
            if ($variation) {
                $price = $variation->get_price();
                $regular_price = $variation->get_regular_price();
                $sale_price = $variation->get_sale_price();

                if ('' !== $price) {
                    $prices['price'][$child_id] = $price;
                }
                if ('' !== $regular_price) {
                    $prices['regular_price'][$child_id] = $regular_price;
                }
                if ('' !== $sale_price) {
                    $prices['sale_price'][$child_id] = $sale_price;
                }
            }
        }

        // Sort prices
        asort($prices['price']);
        asort($prices['regular_price']);
        asort($prices['sale_price']);

        $this->prices_array = $prices;

        return $this->prices_array;
    }

    /**
     * Get available variations for this product
     * Override because WC_Product_Variable uses data_store which doesn't work
     * properly with custom variable product types
     */
    public function get_available_variations($return = 'array')
    {
        $children = $this->get_children();
        $available_variations = [];

        foreach ($children as $child_id) {
            $variation = wc_get_product($child_id);

            if (!$variation) {
                continue;
            }

            // Check if variation should be available
            if (!$variation->exists()) {
                continue;
            }

            // Check if purchasable (has price)
            if (!$variation->is_purchasable()) {
                continue;
            }

            // Build variation data
            if ($return === 'array') {
                $variationData = $this->get_available_variation($variation);
                // Override availability_html to be empty for licensed products
                $variationData['availability_html'] = '';
                $available_variations[] = $variationData;
            } else {
                $available_variations[] = $variation;
            }
        }

        if ($return === 'array') {
            $available_variations = array_values(array_filter($available_variations));
        }

        return $available_variations;
    }

    /**
     * Get max activations for this product (parent default)
     * Falls back to default settings if not set on product
     */
    public function get_max_activations(): int
    {
        $value = $this->get_meta('_licensed_max_activations', true);
        if ($value !== '' && $value !== null) {
            return max(1, (int) $value);
        }
        return SettingsController::getDefaultMaxActivations();
    }

    /**
     * Check if product has custom max activations set
     */
    public function has_custom_max_activations(): bool
    {
        $value = $this->get_meta('_licensed_max_activations', true);
        return $value !== '' && $value !== null;
    }

    /**
     * Get validity days (parent default - variations override this)
     * Falls back to default settings if not set on product
     */
    public function get_validity_days(): ?int
    {
        $value = $this->get_meta('_licensed_validity_days', true);
        if ($value !== '' && $value !== null) {
            return (int) $value > 0 ? (int) $value : null;
        }
        return SettingsController::getDefaultValidityDays();
    }

    /**
     * Check if product has custom validity days set
     */
    public function has_custom_validity_days(): bool
    {
        $value = $this->get_meta('_licensed_validity_days', true);
        return $value !== '' && $value !== null;
    }

    /**
     * Check if license should be bound to major version
     * Falls back to default settings if not set on product
     */
    public function is_bound_to_version(): bool
    {
        $value = $this->get_meta('_licensed_bind_to_version', true);
        if ($value !== '' && $value !== null) {
            return $value === 'yes';
        }
        return SettingsController::getDefaultBindToVersion();
    }

    /**
     * Check if product has custom bind to version setting
     */
    public function has_custom_bind_to_version(): bool
    {
        $value = $this->get_meta('_licensed_bind_to_version', true);
        return $value !== '' && $value !== null;
    }

    /**
     * Get current software version (derived from latest product version)
     */
    public function get_current_version(): string
    {
        $versionManager = new VersionManager();
        $latestVersion = $versionManager->getLatestVersion($this->get_id());

        return $latestVersion ? $latestVersion->getVersion() : '';
    }

    /**
     * Get major version number from version string
     */
    public function get_major_version(): int
    {
        $versionManager = new VersionManager();
        $latestVersion = $versionManager->getLatestVersion($this->get_id());

        if ($latestVersion) {
            return $latestVersion->getMajorVersion();
        }

        return 1;
    }
}
