<?php

namespace FuseCommerce\Models;

use FuseCommerce\Config\IndexingConfig;

/**
 * Model representing the state of a product indexing process
 */
class IndexingState {
    public string $process_id;
    public string $status; // 'working' | 'completed' | 'error'
    public int $started_at;
    public int $updated_at;
    public int $offset;
    public int $total_processed;
    public int $total_products;
    public int $batch_size;
    public string $phase; // 'taxonomies' | 'products'
    public array $errors;

    /**
     * Status constants
     */
    const STATUS_WORKING = 'working';
    const STATUS_COMPLETED = 'completed';
    const STATUS_ERROR = 'error';

    /**
     * Phase constants
     */
    const PHASE_TAXONOMIES = 'taxonomies';
    const PHASE_PRODUCTS = 'products';

    /**
     * Create a new indexing state
     */
    public function __construct() {
        $this->process_id = $this->generateProcessId();
        $this->status = self::STATUS_WORKING;
        $this->started_at = time();
        $this->updated_at = time();
        $this->offset = 0;
        $this->total_processed = 0;
        $this->total_products = 0;
        $this->batch_size = IndexingConfig::INDEXING_BATCH_SIZE;
        $this->phase = self::PHASE_TAXONOMIES;
        $this->errors = [];
    }

    /**
     * Generate a unique process ID
     *
     * @return string UUID v4
     */
    private function generateProcessId(): string {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            wp_rand( 0, 0xffff ),
            wp_rand( 0, 0xffff ),
            wp_rand( 0, 0xffff ),
            wp_rand( 0, 0x0fff ) | 0x4000,
            wp_rand( 0, 0x3fff ) | 0x8000,
            wp_rand( 0, 0xffff ),
            wp_rand( 0, 0xffff ),
            wp_rand( 0, 0xffff )
        );
    }

    /**
     * Convert state to array for storage
     *
     * @return array
     */
    public function toArray(): array {
        return [
            'process_id'      => $this->process_id,
            'status'          => $this->status,
            'started_at'      => $this->started_at,
            'updated_at'      => $this->updated_at,
            'offset'          => $this->offset,
            'total_processed' => $this->total_processed,
            'total_products'  => $this->total_products,
            'batch_size'      => $this->batch_size,
            'phase'           => $this->phase,
            'errors'          => $this->errors,
        ];
    }

    /**
     * Create state from array
     *
     * @param array $data
     * @return self
     */
    public static function fromArray( array $data ): self {
        $state = new self();

        $state->process_id      = $data['process_id'] ?? $state->process_id;
        $state->status          = $data['status'] ?? $state->status;
        $state->started_at      = $data['started_at'] ?? $state->started_at;
        $state->updated_at      = $data['updated_at'] ?? $state->updated_at;
        $state->offset          = $data['offset'] ?? $state->offset;
        $state->total_processed = $data['total_processed'] ?? $state->total_processed;
        $state->total_products  = $data['total_products'] ?? $state->total_products;
        $state->batch_size      = $data['batch_size'] ?? $state->batch_size;
        $state->phase           = $data['phase'] ?? $state->phase;
        $state->errors          = $data['errors'] ?? $state->errors;

        return $state;
    }

    /**
     * Save state to WordPress option
     *
     * @return bool
     */
    public function save(): bool {
        $this->updated_at = time();
        return update_option( IndexingConfig::INDEXING_STATE_OPTION_KEY, $this->toArray() );
    }

    /**
     * Load state from WordPress option
     *
     * @return self|null Returns null if no state exists
     */
    public static function load(): ?self {
        $data = get_option( IndexingConfig::INDEXING_STATE_OPTION_KEY );

        if ( empty( $data ) || ! is_array( $data ) ) {
            return null;
        }

        return self::fromArray( $data );
    }

    /**
     * Delete state from WordPress option
     *
     * @return bool
     */
    public static function delete(): bool {
        return delete_option( IndexingConfig::INDEXING_STATE_OPTION_KEY );
    }

    /**
     * Check if process is working
     *
     * @return bool
     */
    public function isWorking(): bool {
        return $this->status === self::STATUS_WORKING;
    }

    /**
     * Check if process is completed
     *
     * @return bool
     */
    public function isCompleted(): bool {
        return $this->status === self::STATUS_COMPLETED;
    }

    /**
     * Check if process has errors
     *
     * @return bool
     */
    public function hasError(): bool {
        return $this->status === self::STATUS_ERROR;
    }

    /**
     * Add an error message to the state
     *
     * @param string $error
     * @return void
     */
    public function addError( string $error ): void {
        $this->errors[] = [
            'message'   => $error,
            'timestamp' => time(),
        ];
    }

    /**
     * Get progress percentage
     *
     * @return float
     */
    public function getProgressPercentage(): float {
        if ( $this->total_products === 0 ) {
            return 0.0;
        }

        return round( ( $this->total_processed / $this->total_products ) * 100, 2 );
    }

    /**
     * Check if process is stale (running for more than 1 hour without updates)
     *
     * @return bool
     */
    public function isStale(): bool {
        $one_hour = 3600;
        return $this->isWorking() && ( time() - $this->updated_at ) > $one_hour;
    }

    /**
     * Mark the process as completed
     *
     * @return void
     */
    public function markCompleted(): void {
        $this->status = self::STATUS_COMPLETED;
        $this->updated_at = time();
    }

    /**
     * Save last state to WordPress option
     *
     * @param array $urls
     * @param array $priceRange
     * @param array $currencySettings
     * @return bool
     */
    public function setLastStateData( array $urls = [], array $priceRange = [], array $currencySettings = [] ): bool {
        return update_option( IndexingConfig::INDEXING_LAST_STATE_OPTION_KEY, [
            'process_id'        => $this->process_id,
            'product_url'       => $urls['product_url'] ?? null,
            'category_url'      => $urls['category_url'] ?? null,
            'attribute_url'     => $urls['attribute_url'] ?? null,
            'tag_url'           => $urls['tag_url'] ?? null,
            'duration'          => time() - $this->started_at,
            'price_range'       => $priceRange,
            'currency_settings' => $currencySettings,
        ] );
    }

    public static function getLastStateData(): ?array {
        return get_option( IndexingConfig::INDEXING_LAST_STATE_OPTION_KEY , []);
    }

    /**
     * Get cached price range from last indexing state
     *
     * @return array Price range with 'min' and 'max' keys, or empty array if not available
     */
    public static function getPriceRange(): array {
        $lastState = self::getLastStateData();
        return $lastState['price_range'] ?? [];
    }

    /**
     * Get cached currency settings from last indexing state
     *
     * @return array Currency settings or empty array if not available
     */
    public static function getCurrencySettings(): array {
        $lastState = self::getLastStateData();
        return $lastState['currency_settings'] ?? [];
    }

    /**
     * Mark the process as error
     *
     * @param string $error_message
     * @return void
     */
    public function markError( string $error_message ): void {
        $this->status = self::STATUS_ERROR;
        $this->addError( $error_message );
        $this->updated_at = time();
    }
}
