<?php

declare( strict_types=1 );

namespace Soft_Tehnica\Freya_Sync_Service\Services;

use DateTime;
use DateTimeZone;
use Exception;
use Soft_Tehnica\Freya_Sync_Service\Data\CategoryAvailabilityDay;
use Soft_Tehnica\Freya_Sync_Service\Data\Notification;
use WC_Product;

defined( 'ABSPATH' ) || exit;

class CategoryScheduleService
{

    private const WEEKDAYS = [
                'Luni',
                'Marți',
                'Miercuri',
                'Joi',
                'Vineri',
                'Sâmbătă',
                'Duminică',
            ];

    public function __construct()
    {
        // Validare coș (fără ajax)
        add_filter( 'woocommerce_add_to_cart_validation', [$this, 'validate_cart_item_old'], 10, 2 );
        // Validare coș (cu ajax) - woocommerce >= 7.1
        add_filter( 'woocommerce_store_api_validate_add_to_cart', [$this, 'validate_cart_item_new'] );

        add_action( 'woocommerce_before_add_to_cart_button', [$this, 'on_before_add_to_cart'] );
    }

    /**
     * @hook woocommerce_add_to_cart_validation
     */
    public function validate_cart_item_old(
        bool $passed,
        int $product_id
    ): bool {
        $product = wc_get_product( $product_id );

        if ( !$product ) {
            return $passed;
        }

        try {
            $this->check_categories_availability( $product );
        } catch ( Exception $e ) {
            NotificationManager::get()->push( new Notification(
                'error',
                sprintf( 'Nu am putut adăuga produsul în coș: %s', $e->getMessage() )
            ) );

            return false;
        }

        return $passed;
    }

    /**
     * @hook woocommerce_store_api_validate_add_to_cart
     */
    public function validate_cart_item_new( WC_Product $product ): void
    {
        try {
            $this->check_categories_availability( $product );
        } catch ( Exception $e ) {
            throw new Exception( sprintf( 'Nu am putut adăuga produsul în coș: %s', $e->getMessage() ), 500, $e );
        }
    }

    public function on_before_add_to_cart(): void
    {
        global $product;
        /** @var WC_Product $product */
        $availabilities = $this->get_availability_for_today( $product );

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

        foreach ( $availabilities as $availability ) {
            try {
                $this->check_availability( $availability );
            } catch ( Exception $e ) {
                $start = '';
                $end   = '';

                if ( !is_null( $availability->start ) ) {
                    $start = $availability->start->format( DATE_ATOM );
                }

                if ( !is_null( $availability->end ) ) {
                    $end = $availability->end->format( DATE_ATOM );
                }
                ?>
                <div id="fss-availability"
                    class="fss-hide"
                    data-category-id="<?php echo $availability->category->term_id; ?>"
                    data-category-name="<?php echo $availability->category->name; ?>"
                    data-available-open="<?php echo intval( $availability->open ); ?>"
                    data-available-start="<?php echo $start; ?>"
                    data-available-end="<?php echo $end; ?>"
                ></div>
                <?php
                break;
            }
        }
    }

    /**
     * @throws Exception
     */
    private function check_categories_availability( WC_Product $product ): void
    {
        $availabilities = $this->get_availability_for_today( $product );

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

        foreach ( $availabilities as $availability ) {
            $this->check_availability( $availability );
        }
    }

    /**
     * @throws Exception
     */
    private function check_availability( CategoryAvailabilityDay $availability ): void
    {
        $weekdayIndex = intval( date( 'w' ) - 1 );
        $now          = new DateTime( 'now', new DateTimeZone( 'UTC' ) );

        if ( !$availability->open ) {
            throw new Exception( sprintf( 'Categoria %s nu este disponibilă în ziua de %s', html_entity_decode( $availability->category->name ), self::WEEKDAYS[$weekdayIndex] ), 500 );
        }

        if (
            is_null( $availability->start )
            && is_null( $availability->end )
        ) {
            return;
        }

        $roStart = !is_null( $availability->start ) ? clone $availability->start : null;
        $roEnd   = !is_null( $availability->end ) ? clone $availability->end : null;
        $roTz    = new DateTimeZone( 'Europe/Bucharest' );

        if ( !is_null( $roStart ) ) {
            $roStart->setTimezone( $roTz );
        }

        if ( !is_null( $roEnd ) ) {
            $roEnd->setTimezone( $roTz );
        }

        // Dacă ambele ore sunt egale, atunci dăm skip la verificare
        if ( $roStart !== null && $roEnd !== null && $roStart == $roEnd ) {
            return;
        }

        $exception = new Exception(
            sprintf(
                'Categoria %s este disponibilă în intervalul orar %s - %s',
                html_entity_decode( $availability->category->name ),
                !is_null( $roStart )
                    ? $roStart->format( 'H:i' )
                    : '00:00',
                !is_null( $roEnd )
                    ? $roEnd->format( 'H:i' )
                    : '23:59'
            ),
            500
        );

        if ( !is_null( $availability->start ) && $now < $availability->start ) {
            throw $exception;
        }

        if ( !is_null( $availability->end ) && $now > $availability->end ) {
            throw $exception;
        }
    }

    /**
     * @return CategoryAvailabilityDay[]
     */
    private function get_availability_for_today( WC_Product $product ): array
    {
        $weekdayIndex = intval( date( 'w' ) - 1 );

        /** @var \WP_Term[] $categories */
        $categories = wp_get_post_terms( $product->get_id(), 'product_cat' );

        $ret = [];

        foreach ( $categories as $category ) {
            $meta = get_term_meta( $category->term_id, 'freya_workdays', true );

            if ( !$meta ) {
                continue;
            }

            $parsed = json_decode( $meta );

            if ( !$parsed ) {
                continue;
            }

            if ( !array_key_exists( $weekdayIndex, $parsed ) ) {
                continue;
            }

            $data = $parsed[$weekdayIndex];

            $start = $data->starting_minute ?? 0;
            $end   = $data->ending_minute ?? 0;

            $startHour   = intval( $start / 60 );
            $startMinute = intval( $start % 60 );
            $endHour     = intval( $end / 60 );
            $endMinute   = intval( $end % 60 );

            $availabilityStart = new DateTime( 'now', new DateTimeZone( 'Europe/Bucharest' ) );
            $availabilityStart->setTime( $startHour, $startMinute );
            $availabilityStart->setTimezone( new DateTimeZone( 'UTC' ) );

            $availabilityEnd = new DateTime( 'now', new DateTimeZone( 'Europe/Bucharest' ) );
            $availabilityEnd->setTime( $endHour, $endMinute );
            $availabilityEnd->setTimezone( new DateTimeZone( 'UTC' ) );

            $ret[] = new CategoryAvailabilityDay(
                $category,
                property_exists( $data, 'open' )
                    ? boolval( $data->open )
                    : true,
                $start !== 0 ? $availabilityStart : null,
                $end !== 0 ? $availabilityEnd : null
            );
        }

        return $ret;
    }
}
