<?php

namespace DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\DAO;

use DropshippingExportFreeVendor\Monolog\Logger;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\Entity\Export;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\Factory\ExportFactory;
use WP_Post;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\Infrastructure\DAO\Exception\NotFoundException;
use DropshippingExportFreeVendor\WPDesk\Library\DropshippingExport\Infrastructure\DAO\Exception\NotSavedException;
class ExportDAO
{
    public const UID = 'uid';
    public const META_KEY_STATUS = 'status';
    public const META_KEY_URL = 'url';
    public const META_KEY_LAST_POSITION = 'last_position';
    public const META_KEY_PRODUCTS_COUNT = 'products_count';
    public const META_KEY_NEXT_EXPORT = 'next_export';
    public const META_KEY_START_DATE = 'start_date';
    public const META_KEY_END_DATE = 'end_date';
    public const META_KEY_EXPORT_TYPE = 'export_type';
    public const META_KEY_EXPORT_CREATED = 'created';
    public const META_KEY_EXPORT_UPDATED = 'updated';
    public const META_KEY_EXPORT_SKIPPED = 'skipped';
    public const META_KEY_CRON_SCHEDULE = 'cron_schedule';
    public const META_KEY_ERROR_MESSAGE = 'error_message';
    public const META_KEY_EXPORT_NAME = 'export_name';
    private ExportFactory $export_factory;
    private Logger $logger;
    public function __construct(Logger $logger, ExportFactory $export_factory)
    {
        $this->logger = $logger;
        $this->export_factory = $export_factory;
    }
    public function add(Export $entity)
    {
        wc_transaction_query('start');
        $post_id = wp_insert_post(['post_title' => wp_strip_all_tags($entity->get_uid()), 'post_status' => 'publish', 'post_content' => ' ', 'post_author' => get_current_user_id(), 'post_type' => $entity->get_post_type()], \true);
        if (!is_wp_error($post_id)) {
            foreach ($this->get_meta_keys() as $key) {
                $method_name = 'get_' . strtolower($key);
                if (method_exists($entity, $method_name)) {
                    update_post_meta($post_id, $key, $entity->{$method_name}());
                }
            }
            wc_transaction_query('commit');
        } else {
            wc_transaction_query('rollback');
            throw new NotSavedException('Error, exported file can\'t be saved');
        }
    }
    public function update(Export $entity)
    {
        foreach ($this->get_meta_keys() as $key) {
            $method_name = 'get_' . strtolower($key);
            if (method_exists($entity, $method_name)) {
                update_post_meta($entity->get_id(), $key, $entity->{$method_name}());
            }
        }
    }
    public function is_uid_exists(string $uid): bool
    {
        try {
            $this->find_by_uid($uid);
            return \true;
        } catch (NotFoundException $e) {
            $this->logger->notice($e->getMessage());
            return \false;
        }
    }
    public function find_by_id(int $id): Export
    {
        $post = $this->find_export_post_by_id($id);
        return $this->export_factory->create($this->fetch_properties_from_post($post));
    }
    public function find_by_uid(string $uid): Export
    {
        $posts = get_posts(['post_type' => Export::POST_TYPE_SLUG, 'post_status' => 'publish', 'numberposts' => 1, 's' => $uid]);
        if (empty($posts)) {
            throw new NotFoundException('Export not exists');
        }
        return $this->export_factory->create($this->fetch_properties_from_post(reset($posts)));
    }
    public function remove_by_id(int $id)
    {
        $post = $this->find_export_post_by_id($id);
        wp_delete_post($post->ID, \true);
    }
    public function has_next_to_export(): bool
    {
        try {
            $this->find_next_to_export();
            return \true;
        } catch (NotFoundException $e) {
            $this->logger->notice($e->getMessage());
            return \false;
        }
    }
    public function find_next_to_export(): Export
    {
        $posts = get_posts(['post_type' => Export::POST_TYPE_SLUG, 'post_status' => 'publish', 'numberposts' => 1, 'orderby' => self::META_KEY_START_DATE, 'order' => 'ASC', 'meta_query' => [
            //phpcs:ignore
            'relation' => 'AND',
            [
                'key' => self::META_KEY_NEXT_EXPORT,
                'value' => \current_time('timestamp'),
                //phpcs:ignore
                'compare' => '<',
            ],
            ['key' => self::META_KEY_STATUS, 'value' => Export::STATUS_WAITING, 'compare' => 'LIKE'],
        ]]);
        if (empty($posts)) {
            throw new NotFoundException('Export list is empty.');
        }
        return $this->export_factory->create($this->fetch_properties_from_post(reset($posts)));
    }
    public function has_export_processing(): bool
    {
        try {
            $this->find_processing_export();
            return \true;
        } catch (NotFoundException $e) {
            $this->logger->notice($e->getMessage());
            return \false;
        }
    }
    public function find_processing_export(): Export
    {
        $posts = get_posts(['post_type' => Export::POST_TYPE_SLUG, 'post_status' => 'publish', 'numberposts' => 1, 'orderby' => self::META_KEY_START_DATE, 'order' => 'ASC', 'meta_query' => [
            //phpcs:ignore
            ['key' => self::META_KEY_STATUS, 'value' => [Export::STATUS_WAITING, Export::STATUS_STOPPED], 'compare' => 'NOT IN'],
        ]]);
        if (empty($posts)) {
            throw new NotFoundException('There is no import in progress.');
        }
        return $this->export_factory->create($this->fetch_properties_from_post(reset($posts)));
    }
    /**
     * @param WP_Post $post
     *
     * @return array, assocciative array with meta keys (as keys) and meta values (as values).
     */
    private function fetch_properties_from_post(WP_Post $post): array
    {
        $post_values = ['id' => $post->ID, 'uid' => $post->post_title, 'date_created' => $post->post_date, 'user_id' => (int) $post->post_author];
        $meta_values = [];
        foreach ($this->get_meta_keys() as $key) {
            $value = get_post_meta($post->ID, $key, \true);
            if (!empty($value)) {
                $meta_values[$key] = $value;
            }
        }
        return array_merge($post_values, $meta_values);
    }
    /**
     * @return Export[]
     */
    public function get_all(): array
    {
        $result = [];
        $posts = get_posts(['post_type' => Export::POST_TYPE_SLUG, 'post_status' => 'publish', 'numberposts' => -1]);
        foreach ($posts as $post) {
            $result[] = $this->export_factory->create($this->fetch_properties_from_post($post));
        }
        return $result;
    }
    private function get_meta_keys(): array
    {
        return [self::META_KEY_STATUS, self::META_KEY_URL, self::META_KEY_LAST_POSITION, self::META_KEY_PRODUCTS_COUNT, self::META_KEY_NEXT_EXPORT, self::META_KEY_START_DATE, self::META_KEY_END_DATE, self::META_KEY_EXPORT_TYPE, self::META_KEY_EXPORT_CREATED, self::META_KEY_EXPORT_UPDATED, self::META_KEY_EXPORT_SKIPPED, self::META_KEY_CRON_SCHEDULE, self::META_KEY_ERROR_MESSAGE, self::META_KEY_EXPORT_NAME];
    }
    private function find_export_post_by_id(int $id): WP_Post
    {
        $posts = get_posts(['post__in' => [$id], 'post_type' => Export::POST_TYPE_SLUG, 'post_status' => 'publish', 'numberposts' => 1]);
        if (empty($posts)) {
            throw new NotFoundException('Export not exists');
        }
        return reset($posts);
    }
}
