<?php

namespace FuseCommerce\Repositories;

/**
 * Repository for querying WooCommerce products
 */
class ProductRepository {

    /**
     * Get total count of published products
     *
     * @return int Total product count
     */
    public function getPublishedProductCount(): int {
        global $wpdb;

        $count = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(ID)
                FROM {$wpdb->posts}
                WHERE post_type = %s
                AND post_status = %s
                AND post_parent = %d",
                'product',
                'publish',
                0
            )
        );

        return (int) $count;
    }

    /**
     * Get a batch of products with offset
     *
     * @param int $offset Starting offset
     * @param int $limit Number of products to fetch
     * @return array Array of WC_Product objects
     */
    public function getProductBatch( int $offset, int $limit ): array {
        $args = [
            'post_type'      => 'product',
            'post_status'    => 'publish',
            'posts_per_page' => $limit,
            'offset'         => $offset,
            'orderby'        => 'ID',
            'order'          => 'ASC',
            'post_parent'    => 0, // Exclude variations
            'fields'         => 'ids', // Get IDs first for better performance
        ];

        $query = new \WP_Query( $args );

        $products = [];

        if ( $query->have_posts() ) {
            foreach ( $query->posts as $product_id ) {
                $product = wc_get_product( $product_id );

                if ( $product && $product instanceof \WC_Product ) {
                    $products[] = $product;
                }
            }
        }

        wp_reset_postdata();

        return $products;
    }

    /**
     * Prime WordPress term caches for a batch of products
     * This reduces database queries when accessing product terms
     *
     * @param array $product_ids Array of product IDs
     * @return void
     */
    public function primeProductCaches( array $product_ids ): void {
        if ( empty( $product_ids ) ) {
            return;
        }

        // Get all product attribute taxonomies
        $attribute_taxonomies = wc_get_attribute_taxonomies();
        $taxonomies           = [ 'product_cat', 'product_tag' ];

        foreach ( $attribute_taxonomies as $tax ) {
            $taxonomies[] = wc_attribute_taxonomy_name( $tax->attribute_name );
        }

        // Prime term cache for all taxonomies
        \update_object_term_cache( $product_ids, 'product' );

        // Additionally, prime each taxonomy cache individually for better coverage
        foreach ( $taxonomies as $taxonomy ) {
            $terms = \wp_get_object_terms( $product_ids, $taxonomy, [ 'fields' => 'all_with_object_id' ] );
        }
    }

    /**
     * Get all product attribute taxonomies that are used by published products
     *
     * @return array Array of taxonomy names (e.g., ['pa_color', 'pa_size'])
     */
    public function getUsedAttributeTaxonomies(): array {
        global $wpdb;

        // Get all registered product attributes
        $attribute_taxonomies = wc_get_attribute_taxonomies();

        if ( empty( $attribute_taxonomies ) ) {
            return [];
        }

        $taxonomy_names = [];
        foreach ( $attribute_taxonomies as $tax ) {
            $taxonomy_names[] = wc_attribute_taxonomy_name( $tax->attribute_name );
        }

        $placeholders = implode( ', ', array_fill( 0, count( $taxonomy_names ), '%s' ) );

        // Query to find which taxonomies are actually used by published products

        $used_taxonomies = $wpdb->get_col( $wpdb->prepare(
            "SELECT DISTINCT tt.taxonomy
            FROM {$wpdb->term_taxonomy} tt
            INNER JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
            INNER JOIN {$wpdb->posts} p ON tr.object_id = p.ID
            WHERE p.post_type = 'product'
            AND p.post_status = 'publish'
            AND p.post_parent = 0
            AND tt.taxonomy IN ($placeholders)",
            $taxonomy_names
        ) );

        return $used_taxonomies;
    }

    /**
     * Get all category IDs used by published products
     *
     * @return array Array of term IDs
     */
    public function getUsedCategoryIds(): array {
        global $wpdb;

        $term_ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT DISTINCT tt.term_id
            FROM {$wpdb->term_taxonomy} tt
            INNER JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
            INNER JOIN {$wpdb->posts} p ON tr.object_id = p.ID
            WHERE p.post_type = %s
            AND p.post_status = %s
            AND p.post_parent = %d
            AND tt.taxonomy = %s",
            'product',
            'publish',
            0,
            'product_cat'
        ) );

        return array_map( 'intval', $term_ids );
    }

    /**
     * Get all tag IDs used by published products
     *
     * @return array Array of term IDs
     */
    public function getUsedTagIds(): array {
        global $wpdb;

        $term_ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT DISTINCT tt.term_id
            FROM {$wpdb->term_taxonomy} tt
            INNER JOIN {$wpdb->term_relationships} tr ON tt.term_taxonomy_id = tr.term_taxonomy_id
            INNER JOIN {$wpdb->posts} p ON tr.object_id = p.ID
            WHERE p.post_type = %s
            AND p.post_status = %s
            AND p.post_parent = %d
            AND tt.taxonomy = %s",
            'product',
            'publish',
            0,
            'product_tag'
        ) );

        return array_map( 'intval', $term_ids );
    }

    /**
     * Check if a product exists and is published
     *
     * @param int $product_id Product ID
     * @return bool
     */
    public function productExists( int $product_id ): bool {
        $product = wc_get_product( $product_id );

        return $product && $product->get_status() === 'publish';
    }

    /**
     * Get all variations for a variable product
     *
     * @param int $parent_id Parent product ID
     * @return array Array of WC_Product_Variation objects
     */
    public function getProductVariations( int $parent_id ): array {
        $product = wc_get_product( $parent_id );

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

        $variation_ids = $product->get_children();
        $variations    = [];

        foreach ( $variation_ids as $variation_id ) {
            $variation = wc_get_product( $variation_id );

            if ( $variation && $variation instanceof \WC_Product_Variation ) {
                $variations[] = $variation;
            }
        }

        return $variations;
    }

    /**
     * Get count of variations for a variable product
     *
     * @param int $parent_id Parent product ID
     * @return int Number of variations
     */
    public function getVariationCount( int $parent_id ): int {
        $product = wc_get_product( $parent_id );

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

        return count( $product->get_children() );
    }

    /**
     * Prime variation data caches for a batch of products
     * This reduces database queries when accessing variation data
     *
     * @param array $product_ids Array of parent product IDs
     * @return void
     */
    public function primeVariationCaches( array $product_ids ): void {
        if ( empty( $product_ids ) ) {
            return;
        }

        global $wpdb;

        // Get all variation IDs for these parent products
        $placeholders = implode( ', ', array_fill( 0, count( $product_ids ), '%d' ) );

        $variation_ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT ID
            FROM {$wpdb->posts}
            WHERE post_type = 'product_variation'
            AND post_parent IN ($placeholders)",
            ...$product_ids
        ) );

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

        // Prime post meta cache for variations
        update_meta_cache( 'post', $variation_ids );

        // Prime object cache for variations
        foreach ( $variation_ids as $variation_id ) {
            wc_get_product( $variation_id );
        }
    }
}
