<?php

namespace Services;

use Exception;
use Services\dtos\AttributeDto;
use services\dtos\CartDto;
use services\dtos\CartProductDto;
use Services\dtos\CategoryDto;
use Services\dtos\CommentDto;
use Services\dtos\FilterDto;
use Services\dtos\ImageDto;
use Services\dtos\ProductDto;
use Services\dtos\ProductDetailsDto;
use services\dtos\VariationDto;
use Services\enums\SliderType;
use Services\enums\SortType;
use WC_Cart;
use WC_Data_Store;
use WP_Exception;
use WP_Query;
use WC_Product;
use WP_Term;

interface ProductServiceInterface
{

    /* @return  ProductDto[] */
    public function search(string $search_term): array;

    /* @return array{products: ProductDto[], takhfif_plus_date: string} */
    public function getDailySpecialProduct(SliderType $type): array;

    /* @return array{products: ProductDto[], category: ?CategoryDto} */
    public function getSliderCatOne(SliderType $type): array;

    // product page

    /**
     * Get details of a single product by ID.
     *
     * @param int $product_id
     *
     * @return ?ProductDetailsDto
     */
    public function getDetailsProduct(int $product_id): ?ProductDetailsDto;

    /** @param int $product_id
     * @param int $limit
     * @return ProductDto[]
     */
    function getRelatedProducts(int $product_id, int $limit): array;
}

class ProductService implements ProductServiceInterface
{

    /**
     * @return ProductDto[]
     */
    public function search(string $search_term): array
    {
        $args = [
            's' => $search_term,
            'posts_per_page' => 5, // TODO
            'post_type' => 'product',
            'post_status' => 'publish',
            'tax_query' => [
                [
                    'taxonomy' => 'product_type',
                    'field' => 'slug',
                    'terms' => ['simple', 'variable'],
                    'operator' => 'IN',
                ],
            ],
        ];

        $wc_query = new WP_Query($args);
        /** @var ProductDto[] */
        $result = [];

        if ($wc_query->have_posts()) {
            while ($wc_query->have_posts()) {
                $wc_query->the_post();
                $product_id = get_the_ID();
                $wc_product = wc_get_product($product_id);

                if ($wc_product instanceof WC_Product) {
                    $product_dto = $this->convertToDto($wc_product);

                    $result[] = $product_dto;
                }
            }
            wp_reset_postdata();
        }

        return $result;
    }

    /**
     * @return ProductDto[]
     */
    public function getProducts(array $product_ids): array
    {
        $args = array(
            'post_type' => 'product',
            'post__in' => $product_ids,
        );
        $wc_query = new WP_Query($args);

        /** @var ProductDto[] */
        $result = [];

        if ($wc_query->have_posts()) {
            while ($wc_query->have_posts()) {
                $wc_query->the_post();
                $product_id = get_the_ID();
                $wc_product = wc_get_product($product_id);

                if ($wc_product instanceof WC_Product) {
                    $product_dto = $this->convertToDto($wc_product);
                    $result[] = $product_dto;
                }
            }
            wp_reset_postdata();
        }

        return $result;
    }


    /**
     * @return array{products: ProductDto[], takhfif_plus_date: string}
     */
    public function getDailySpecialProduct(SliderType $type): array
    {
        $daily_special_products = $type->value;
        $group_field = get_field($daily_special_products, 'option') ?: [];

        $slider_ids = [];
        $takhfif_plus_date = '0';

        // Check if group_field is an array and contains offer-slider-main
        if (is_array($group_field) && isset($group_field['offer-slider-main'])) {
            $repeater = $group_field['offer-slider-main'];
            $takhfif_plus_date = $group_field['takhfif_plus_date'] ?? '0';

            if (is_array($repeater) && !empty($repeater)) {
                foreach ($repeater as $row) {
                    $product_id = $row['special-product'] ?? null;
                    if (is_numeric($product_id)) {
                        $slider_ids[] = (int)$product_id;
                    }
                }
            }
        }

        $products = [];

        if (!empty($slider_ids)) {
            $args = [
                'post_type' => 'product',
                'post__in' => $slider_ids,
                'posts_per_page' => 12,
                'post_status' => 'publish',
            ];

            $wc_query = new WP_Query($args);

            /** @var ProductDto[] */
            $products = [];

            if ($wc_query->have_posts()) {
                while ($wc_query->have_posts()) {
                    $wc_query->the_post();
                    $product_id = get_the_ID();
                    $wc_product = wc_get_product($product_id);

                    if ($wc_product && $wc_product instanceof WC_Product) {
                        $product_dto = $this->convertToDto($wc_product);
                        $products[] = $product_dto;
                    }
                }
                wp_reset_postdata();
            }
        }

        return [
            'products' => $products,
            'takhfif_plus_date' => $takhfif_plus_date
        ];
    }

    /**
     * @return array{products: ProductDto[], category: ?CategoryDto}
     */
    public function getSliderCatOne(SliderType $type): array
    {
        $cat_id_slider_one = $type->value;
        $cat_id = get_field($cat_id_slider_one, 'option');

        $cat_id = is_numeric($cat_id) ? (int)$cat_id : 0;

        if ($cat_id <= 0) {
            return ['products' => [], 'category' => null];
        }

        $category_term = get_term($cat_id, 'product_cat');
        $category_dto = null;

        if ($category_term instanceof WP_Term) {
            $category_title = $category_term->name;
            $category_link = get_term_link($category_term) ?: '';
            $category_image_id = get_term_meta($cat_id, 'thumbnail_id', true);
            $category_image_dto = empty($category_image_id) ? null :
                ImageDto::build($category_image_id, 'full', 'دسته بندی');

            $category_dto = new CategoryDto($category_image_dto, $category_link, $category_title);
        }


        $args = [
            'post_type' => 'product',
            'tax_query' => [
                [
                    'taxonomy' => 'product_cat',
                    'field' => 'term_id',
                    'terms' => $cat_id,
                ],
            ],
            'posts_per_page' => 12,
            'post_status' => 'publish',
            'ignore_sticky_posts' => true,
        ];

        $wc_query = new WP_Query($args);

        /** @var ProductDto[] */
        $products_result = [];

        if ($wc_query->have_posts()) {
            while ($wc_query->have_posts()) {
                $wc_query->the_post();
                $product_id = get_the_ID();
                $wc_product = wc_get_product($product_id);

                if ($wc_product instanceof WC_Product) {
                    $product_dto = $this->convertToDto($wc_product);
                    $products_result[] = $product_dto;
                }
            }
            wp_reset_postdata();
        }

        // --- 2. Return the new structure ---
        return [
            'products' => $products_result,
            'category' => $category_dto,
        ];
    }


    /**
     * Get details of a single product by ID.
     *
     * @param int $product_id
     *
     * @return ?ProductDetailsDto
     */
    public function getDetailsProduct(int $product_id): ?ProductDetailsDto
    {
        if (empty($product_id)) {
            error_log('getDetailsProduct: Invalid product ID');

            return null;
        }

        $args = [
            'post_type' => 'product',
            'post__in' => [$product_id],
            'posts_per_page' => 1,
            'post_status' => 'publish',
        ];

        $wc_query = new WP_Query($args);

        if ($wc_query->have_posts()) {
            while ($wc_query->have_posts()) {
                $wc_query->the_post();
                $post_id = get_the_ID();
                $wc_product = wc_get_product($post_id);

                if ($wc_product && $wc_product instanceof WC_Product) {
                    $title = $wc_product->get_name() ?: 'بدون عنوان';

                    $image_id = $wc_product->get_image_id();
                    $image = empty($image_id) ? null : ImageDto::build($image_id, 'full', 'محصول');

                    $link = get_permalink($post_id) ?: '';

                    $categories = wp_get_post_terms($post_id, 'product_cat', ['fields' => 'names']);
                    $category = !empty($categories) ? implode(', ', $categories) : 'بدون دسته‌بندی';

                    $gallery_ids = $wc_product->get_gallery_image_ids();
                    $gallery = array_map(function ($id) {
                        return ImageDto::build($id, 'full', 'محصول');
                    }, $gallery_ids);

                    $short_desc_attr = $wc_product->get_short_description() ?: '';
                    $full_desc = $wc_product->get_description() ?: '';

                    $in_stock = $wc_product->is_in_stock();

                    $regular_price = (float)($wc_product->get_regular_price());
                    $sale_price = (float)($wc_product->get_sale_price() ?: $regular_price);

                    $wc_attributes = $wc_product->get_attributes('view');
                    $wc_attributes = array_filter($wc_attributes, fn($attr) => $attr['visible']);
                    usort($wc_attributes, function ($attr1, $attr2) {
                        return $attr1['position'] <=> $attr2['position'];
                    });
                    $attributes = array_map(function ($attr) use ($wc_product) {
                        if (str_starts_with($attr['name'], 'pa_')) {
                            $attribute_label = wc_attribute_label($attr['name']);
                            $attribute_values = explode(',', $wc_product->get_attribute($attr['name']));
                            return new AttributeDto($attr['id'], $attribute_label, $attribute_values, $attr['variation']);
                        }

                        return new AttributeDto($attr['id'], $attr['name'], $attr['options'], $attr['variation']);
                    }, $wc_attributes);

                    $min_quantity = $wc_product->get_min_purchase_quantity();
                    $max_quantity = $wc_product->get_max_purchase_quantity();

                    $variations = [];
                    if ($wc_product->is_type('variable')) {
                        $default_variation_id = $this->get_default_variation_id($wc_product);

                        foreach ($wc_product->get_children() as $variation_id) {
                            $wc_variation = wc_get_product($variation_id);
                            if ($wc_variation instanceof WC_Product) {
                                $variation_image_id = $wc_variation->get_image_id();
                                $variation_image = empty($image_id) ? null : ImageDto::build($variation_image_id, 'full', 'محصول');

                                $variation_regular_price = (float)($wc_variation->get_regular_price());
                                $variation_sale_price = (float)($wc_variation->get_sale_price() ?: $variation_regular_price);
                                $variation_in_stock = $wc_variation->is_in_stock();

                                if ($variation_sale_price == 0)
                                    $variation_in_stock = false;

                                if ($default_variation_id === $variation_id) {
                                    $regular_price = $variation_regular_price;
                                    $sale_price = $variation_sale_price;
                                }

                                $variation_attributes = array();
                                foreach ($wc_variation->get_attributes() as $attribute_name => $attribute_value) {
                                    foreach ($attributes as $attribute) {
                                        $attribute_name = urldecode($attribute_name);
                                        $attribute_value = urldecode($attribute_value);
                                        if (str_starts_with($attribute_name, 'pa_')) {
                                            $attribute_name = wc_attribute_label($attribute_name);
                                        }
                                        if ($attribute->name == $attribute_name) {
                                            $variation_attributes[] = new FilterDto($attribute->name, $attribute_value);
                                            break;
                                        }
                                    }
                                }
                                $variations[] = new VariationDto($variation_id, $variation_sale_price,
                                    $variation_regular_price, $variation_in_stock, $variation_attributes, $variation_image);
                            }
                        }
                    }

                    $product_dto = new ProductDetailsDto(
                        $title,
                        $image,
                        $gallery,
                        $category,
                        $short_desc_attr,
                        $in_stock,
                        $full_desc,
                        $regular_price,
                        $sale_price,
                        $link,
                        $attributes,
                        $variations,
                        $min_quantity,
                        $max_quantity,
                    );

                    wp_reset_postdata();

                    return $product_dto;
                }
            }
            wp_reset_postdata();
        }

        error_log('getDetailsProduct: No product found for ID ' . $product_id);

        return null;
    }


    /**
     * Get filtered products for the WooCommerce archive page.
     *
     * @param SortType $sort_type Sorting method (e.g., popularity, date, price).
     * @param int $page_size Number of products per page (e.g., 10).
     * @param int $page_number Current page number (e.g., 2).
     * @param string $search_term Search query (e.g., "لپتاپ سیاه").
     * @param bool $is_available Filter for in-stock products only.
     * @param FilterDto[] $attributes
     *
     * @return array{products: ProductDto[], total_results: int}
     */
    public function getArchiveProducts(
        SortType $sort_type,
        int      $page_size,
        int      $page_number,
        string   $search_term,
        bool     $is_available,
        int      $category_id,
        array    $attributes = []
    ): array
    {
        $args = [
            'post_type' => 'product',
            'posts_per_page' => max(1, $page_size), // Ensure at least 1
            'paged' => max(1, $page_number), // Ensure at least page 1
            'post_status' => 'publish',
        ];

        $args['meta_query'] = ['relation' => 'AND'];
        $args['tax_query'] = ['relation' => 'AND'];

        // Apply search term
        if (!empty($search_term)) {
            $args['s'] = sanitize_text_field($search_term);
        }

        // Apply stock filter
        if ($is_available) {
            $args['meta_query'] = [
                [
                    'key' => '_stock_status',
                    'value' => 'instock',
                    'compare' => '=',
                ],
            ];
        }

        // Apply sorting
        switch ($sort_type) {
            case SortType::Popularity:
                $args['orderby'] = 'meta_value_num';
                $args['meta_key'] = 'total_sales';
                $args['order'] = 'DESC';
                break;
            case SortType::Date:
                $args['orderby'] = 'date';
                $args['order'] = 'DESC';
                break;
            case SortType::PriceAsc:
                $args['meta_key'] = '_price';
                $args['orderby'] = 'meta_value_num';
                $args['order'] = 'ASC';
                break;
            case SortType::PriceDesc:
                $args['meta_key'] = '_price';
                $args['orderby'] = 'meta_value_num';
                $args['order'] = 'DESC';
                break;
        }

        // Apply category filter
        if (!empty($category_id)) {
            // Initialize tax_query if not already set
            if (!isset($args['tax_query'])) {
                $args['tax_query'] = [];
            }

            // Add category filter
            $args['tax_query'][] = [
                'taxonomy' => 'product_cat',
                'field' => 'term_id',
                'terms' => [$category_id],
                'operator' => 'IN',
            ];
        }

        if (!empty($attributes)) {
            $grouped_filters = [];
            foreach ($attributes as $filter) {
                $grouped_filters[str_replace('filter', 'pa', $filter->key)][] = $filter->value;
            }

            foreach ($grouped_filters as $taxonomy => $terms) {
                $args['tax_query'][] = [
                    'taxonomy' => $taxonomy,
                    'field' => 'name',
                    'terms' => $terms,
                    'operator' => 'IN',
                ];
            }
        }

        $wc_query = new WP_Query($args);

        /** @var ProductDto[] */
        $products = [];
        $total_results = $wc_query->found_posts;

        if ($wc_query->have_posts()) {
            while ($wc_query->have_posts()) {
                $wc_query->the_post();
                $product_id = get_the_ID();
                $wc_product = wc_get_product($product_id);

                if ($wc_product && $wc_product instanceof WC_Product) {
                    $products[] = $this->convertToDto($wc_product);
                }
            }
            wp_reset_postdata();
        }

        return [
            'products' => $products,
            'total_results' => $total_results
        ];
    }

    /**
     * @param int $product_id
     * @param int $limit
     * @return ProductDto[]
     */
    function getRelatedProducts(int $product_id, int $limit): array
    {
        $product = wc_get_product($product_id);
        if (!$product) {
            return [];
        }

        $related_products = [];
        $related_ids = wc_get_related_products($product_id, $limit);
        foreach ($related_ids as $related_id) {
            $wc_product = wc_get_product($related_id);

            if ($wc_product && $wc_product instanceof WC_Product) {
                $related_products[] = $this->convertToDto($wc_product);
            }
        }

        return $related_products;
    }

    /**
     * @param int $product_id The ID of the WooCommerce product.
     * @param int $child_count
     * @param int $page_size The maximum number of comments/reviews to return.
     * @param int $page_number
     * @return array{comments:CommentDto[], total: int}
     */
    function getComments(int $post_id, int $child_count, int $page_size, int $page_number): array
    {
        // Same as Post
        $parent_ids = [0];

        /* @var CommentDto[] $comments */
        $comments = [];

        for ($i = 0; $i < 3; $i++) {
            $count = max(1, $i == 0 ? $page_size : $child_count);
            $offset = $i == 0 ? ($page_number - 1) * $count : 0;

            $args = [
                'post_id' => $post_id,
                'number' => $count,
                'offset' => $offset,
                'status' => 'approve',
                'parent__in' => $parent_ids,
                'orderby' => 'comment_date',
                'order' => 'DESC',
            ];

            $wc_comments = get_comments($args);
            if (empty($wc_comments)) break;

            $parent_ids = [];
            foreach ($wc_comments as $wc_comment) {
                $user_id = $wc_comment->user_id;
                $is_admin = false;
                if (!empty($user_id))
                    if (user_can($wc_comment->user_id, 'manage_options'))
                        $is_admin = true;

                $comments[] = new CommentDto(intval($wc_comment->comment_ID), $wc_comment->comment_author, $is_admin,
                    $wc_comment->comment_content, $wc_comment->comment_date, intval($wc_comment->comment_parent));
                $parent_ids[] = intval($wc_comment->comment_ID);
            }
        }

        $parents = [];
        foreach ($comments as $comment1) {
            if ($comment1->parent_id == 0) {
                $parents[] = $comment1;
            }
            foreach ($comments as $comment2) {
                if ($comment1->id == $comment2->parent_id) {
                    $comment1->children[] = $comment2;
                }
            }
        }

        wp_lazyload_comment_meta(array_map(fn($parent) => $parent->id, $parents));
        foreach ($parents as $parent) {
            $parent->rate = intval(get_comment_meta($parent->id, 'rating', true));
        }

        $total = get_comments([
            'post_id' => $post_id,
            'status' => 'approve',
            'parent' => 0,
            'count' => true,
        ]);

        return ['comments' => $parents, 'total' => $total];
    }

    /**
     * Creates a new product review (comment with rating).
     *
     * @param int $post_id The ID of the WooCommerce product.
     * @param string $content The content of the review.
     * @param string $author The name of the author.
     * @param string $author_email The email of the author.
     * @param int|null $rate The star rating (1-5). Can be null if no rating is given.
     * @throws WP_Exception
     */
    function sendProductComment(int $post_id, string $content, string $author, string $author_email, ?int $rate = null): void
    {
        // Same for Post
        $content = trim($content);
        $author = trim($author);
        $author_email = trim($author_email);

        if (empty($content))
            throw new WP_Exception('متن دیدگاه نمی‌تواند خالی باشد.');
        if (empty($author))
            throw new WP_Exception('نام نمی‌تواند خالی باشد.');
        if (!empty($author_email) && !is_email($author_email))
            throw new WP_Exception('ایمیل وارد شده معتبر نیست.');
        if ($rate !== null && ($rate < 1 || $rate > 5))
            throw new WP_Exception('امتیاز باید بین ۱ تا ۵ باشد.');

        $comment_data = [
            'comment_post_ID' => $post_id,
            'comment_author' => sanitize_text_field($author),
            'comment_author_email' => sanitize_email($author_email),
            'comment_content' => wp_kses_post($content),
            'comment_type' => $rate == null ? 'comment' : 'review',
            'user_id' => get_current_user_id(),
        ];

        $comment_id = wp_new_comment($comment_data, true);
        if (is_wp_error($comment_id))
            throw new WP_Exception('مشکلی در ثبت دیدگاه شما به وجود آمده است.');

        if ($rate !== null)
            add_comment_meta($comment_id, 'rating', $rate);
    }

    /**
     * @throws WP_Exception
     */
    function addToCart(int $product_id, int $quantity, int $variation_id = 0): void
    {
        if ($quantity <= 0)
            throw new WP_Exception('تعداد محصول در سبد خرید باید بزرگتر از 0 باشد.');

        try {
            $product = wc_get_product($product_id);
            if ($product->is_type('variable') && empty($variation_id))
                $variation_id = $this->get_default_variation_id($product);

            $res = WC()->cart->add_to_cart($product_id, $quantity, $variation_id);
            if ($res === false)
                throw new Exception();

            do_action('woocommerce_ajax_added_to_cart', $product_id);

        } catch (Exception $ex) {
            throw new WP_Exception('مشکلی در افزودن این محصول به سبد خرید به وجود آمده است.');
        }
    }

    function get_default_variation_id($product)
    {
        $default_attributes = $product->get_default_attributes();

        if (empty($default_attributes))
            return 0;

        $attributes_for_lookup = array();
        foreach ($default_attributes as $key => $value) {
            $attributes_for_lookup['attribute_' . $key] = $value;
        }

        $data_store = WC_Data_Store::load('product');
        $default_variation_id = $data_store->find_matching_product_variation($product, $attributes_for_lookup);

        return $default_variation_id;
    }

    /**
     * @throws WP_Exception
     */
    function removeFromCart(string $item_key): void
    {
        if (WC()->cart->remove_cart_item($item_key))
            do_action('woocommerce_ajax_added_to_cart', 0);
        else
            throw new WP_Exception('مشکلی در حذف این محصول از سبد خرید به وجود آمده است.');
    }

    /**
     * @throws WP_Exception
     */
    function updateCartQuantity(string $item_key, int $quantity): void
    {
        if (WC()->cart->set_quantity($item_key, $quantity))
            do_action('woocommerce_ajax_added_to_cart', 0);
        else
            throw new WP_Exception('مشکلی در تغییر تعداد این محصول در سبد خرید به وجود آمده است.');
    }

    function updateCartCustomerInfo(array $info): void
    {
        $customer = WC()->customer;
        foreach ($info as $key => $value) {
            if($key === "shipping_method[0]") WC()->session->set('chosen_shipping_methods', [$value]);
            if($key === "billing_state") $customer->set_shipping_state($value);
            if($key === "billing_city") $customer->set_shipping_city($value);
            if($key === "billing_postcode") $customer->set_billing_postcode($value);
        }
        do_action('woocommerce_ajax_added_to_cart', 0);
    }

    /**
     * @throws WP_Exception
     */
    function applyCoupon(string $coupon): void
    {
        if (empty($coupon))
            throw new WP_Exception('کد تخفیف وارد نشده است.');

        if (WC()->cart->apply_coupon(sanitize_text_field($coupon)))
            do_action('woocommerce_ajax_added_to_cart', 0);
        else
            throw new WP_Exception('کد تخفیف نامعتبر است.');
    }

    /**
     * @throws WP_Exception
     */
    function removeCoupon(string $coupon): void
    {
        if (empty($coupon))
            throw new WP_Exception('کد تخفیف وارد نشده است.');

        if (WC()->cart->remove_coupon(sanitize_text_field($coupon)))
            do_action('woocommerce_ajax_added_to_cart', 0);
        else
            throw new WP_Exception('مشکلی در حذف کد تخفیف به وجود آمده است.');
    }

    function getCart(): CartDto
    {
        $cart = WC()->cart;
        $cart->calculate_totals();
        $cart_items = $cart->get_cart();

        $subtotal = 0;
        $discount_total = $cart->get_discount_total();
        $shipping_total = $cart->get_shipping_total();
        $fee_total = $cart->get_fee_total();
        $grand_total = $cart->get_total('edit');

        $products_data = [];

        foreach ($cart_items as $cart_item_key => $cart_item) {
            $wc_product = $cart_item['data'];

            if ($wc_product instanceof WC_Product) {
                $product_dto = $this->convertToDto($wc_product);

                $quantity = $cart_item['quantity'];
                $products_data[] = new CartProductDto($product_dto, $quantity, $cart_item_key);

                $subtotal += $product_dto->regular_price * $quantity;
                $discount_total += ($product_dto->regular_price - $product_dto->sale_price) * $quantity;
            }
        }

        return new CartDto($products_data, $subtotal, $discount_total, $shipping_total, $fee_total, $grand_total);
    }

    function convertToDto(WC_Product $wc_product): ProductDto
    {
        $id = $wc_product->get_id();
        $title = $wc_product->get_name();

        $image_id = $wc_product->get_image_id();
        $image = empty($image_id) ? null : ImageDto::build($image_id, 'full', 'محصول');

        $link = get_permalink($wc_product->get_id());

        $categories = wp_get_post_terms($wc_product->get_id(), 'product_cat', ['fields' => 'names']);
        $category = !empty($categories) ? $categories[0] : '';

        $regular_price = (float)$wc_product->get_regular_price();
        $sale_price = (float)($wc_product->get_sale_price() ?: $regular_price);

        $min_quantity = $wc_product->get_min_purchase_quantity();
        $max_quantity = $wc_product->get_max_purchase_quantity();

        if ($wc_product->is_type('variable')) {
            $default_variation_id = $this->get_default_variation_id($wc_product);
            $wc_variation = wc_get_product($default_variation_id);
            $regular_price = $wc_variation->get_regular_price();
            $sale_price = $wc_variation->get_sale_price();
        }

        return new ProductDto(
            $id,
            $title,
            $image,
            $link,
            $category,
            $regular_price,
            $sale_price,
            $min_quantity,
            $max_quantity
        );
    }
}