<?php

namespace CouponsPlus\App\Conditions\BuiltIn\Conditions;

use Carbon\Carbon;
use CouponsPlus\App\Conditions\Condition;
use CouponsPlus\Original\Environment\Env;
use CouponsPlus\Original\Collections\Collection;
use CouponsPlus\Original\Collections\Mapper\Types;

#[\AllowDynamicProperties]
Class CouponUsageNumberOfTimes extends Condition
{
    CONST TYPE = 'CouponUsageNumberOfTimes';
    
    protected /*WC_Coupon*/ $coupon;
    const DATABASE_DATE_FORMAT = 'Y-m-d H:i:s';

    static public function getName() : string
    {
        return __('Usage Limits', 'coupons-plus-for-woocommerce');
    }

    static public function getDescription() : string
    {
        return __('The number of times this coupon can be used across all customers. The limit may be time-based. For example: 10 times per day, week, month, or year. This may also be used to apply tiered discounts based on the number of times the coupon has been used. For example, the first 10 uses of the coupon will get a 20% discount, the next 50 uses, a 15% discount, and so on.', 'coupons-plus-for-woocommerce');
    }

    static public function getUseCases(): Collection
    {
        return new Collection([
            __('Apply different discounts (tiered) based on the coupon usage: EG: First 10 uses: 20% off, First 50 uses 15% OFF...', 'coupons-plus-for-woocommerce'),
            __('Restrict coupon usage per day, week, month or year', 'coupons-plus-for-woocommerce'),
        ]);  
    } 
    
    // test weeks starts based on the given settings:
    // eg if setting is suday, test that week is from sunday to satirday and NOT from monday to sunday
    public static function getOptions() : Collection
    {
        return new Collection([
            'quantity' => [
                'type' => Types::STRING()->allowed([
                    __('Maximum', 'coupons-plus-for-woocommerce') => 'maximum',
                ]),
                'amount' => Types::INTEGER()->meta([
                    'name' => '',
                ])->withDefault(1),
            ],
            'interval' => Types::STRING()->allowed([
                                            __('All Time', 'coupons-plus-for-woocommerce') => 'alltime', 
                                            __('Every Day', 'coupons-plus-for-woocommerce') => 'everyday', 
                                            __('Every Week', 'coupons-plus-for-woocommerce') => 'everyweek', 
                                            __('Every Month', 'coupons-plus-for-woocommerce') => 'everymonth', 
                                            __('Every Year', 'coupons-plus-for-woocommerce') => 'everyyear'
                                        ])
                                        ->withDefault('alltime')
        ]);
    }

    public function setExtraData(\WC_Coupon $coupon)
    {
        $this->coupon = $coupon;
    }

    protected function test() : bool
    {
        (integer) $usageCount = $this->getUsageCount();

        return $usageCount < $this->options->quantity->amount;
    }

    protected function getUsageCount() : int
    {
        global $wpdb;
        $cacheKey = Env::getWithPrefix('coupon_global_usage_number_of_times');

        if (wp_cache_get($cacheKey)) {
            return wp_cache_get($cacheKey);
        }

        /**
         * Direct database call is required here because WooCommerce does not provide
         * an API method to retrieve coupon usage counts filtered by date range.
         * The WC_Coupon::get_usage_count() method only returns all-time totals.
         * This query is secure (using $wpdb->prepare) and cached for performance.
         */
        /**
         * Todo: new hpos uses the wc_orders table instead of wp_posts for orders so change that
         * by checking if HPOS is enabled or not then use the correct table based on that.
         *
         * resources: https://developer.woocommerce.com/docs/features/high-performance-order-storage/
         *            https://github.com/woocommerce/woocommerce/wiki/Database-Description#table-woocommerce_order_items
         */
        $value = (integer) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*)
             FROM {$wpdb->posts} as posts
             JOIN {$wpdb->prefix}woocommerce_order_items as order_items
             ON order_items.order_id = posts.ID
             WHERE order_items.order_item_name = %s
             AND order_items.order_item_type = 'coupon'
             AND posts.post_date BETWEEN %s AND %s
             AND posts.post_type = 'shop_order'
            ",
            $this->coupon->get_code(),
            $startDate = $this->getDateInDATETIMEFormat()['start'],
            $endDate = $this->getDateInDATETIMEFormat()['end']
        ));

        wp_cache_set($cacheKey, $value, '', 60 * 30); // cache for 30 minutes

        return $value;
    }

    protected function getDateInDATETIMEFormat() : array
    {
        (string) $start = '';
        (string) $end = '';
        (object) $today = Carbon::now();

        $today->setWeekStartsAt((integer) get_option('start_of_week'));

        switch ($this->options->interval) {
            case 'alltime':
                $start = '1000-01-01 00:00:00';
                $end = '9999-12-31 23:59:59';
                break;
            case 'everyday':
                $start = $today->startOfDay()->format(static::DATABASE_DATE_FORMAT);
                $end = $today->endOfDay()->format(static::DATABASE_DATE_FORMAT);
                break;
            case 'everyweek':
                $start = $today->startOfWeek()->format(static::DATABASE_DATE_FORMAT);
                $end = $today->endOfWeek()->format(static::DATABASE_DATE_FORMAT);
                break;
            case 'everymonth':
                $start = $today->startOfMonth()->format(static::DATABASE_DATE_FORMAT);
                $end = $today->endOfMonth()->format(static::DATABASE_DATE_FORMAT);
                break;
            case 'everyyear':
                $start = $today->startOfYear()->format(static::DATABASE_DATE_FORMAT);
                $end = $today->endOfYear()->format(static::DATABASE_DATE_FORMAT);
                break;
        }

        return compact(
            'start',
            'end'
        );
    }
}