<?php

declare(strict_types=1);

namespace CloudXM\NFe\Persistence;

use CloudXM\SDK\Support\HasLogger;

/**
 * NFe Queue Repository
 *
 * Manages NFe processing queue records in database
 */
class NfeQueueRepository
{
    use HasLogger;

    private string $table_prefix;

    /**
     * Constructor
     */
    public function __construct(string $table_prefix = 'nfe_')
    {
        $this->table_prefix = $table_prefix;
    }

    /**
     * Get table name
     */
    private function getTableName(): string
    {
        global $wpdb;
        return $wpdb->prefix . $this->table_prefix . 'queue';
    }

    /**
     * Add task to queue
     *
     * @param array $data Queue task data
     * @return int|false Queue ID or false on failure
     */
    public function add(array $data)
    {
        global $wpdb;

        $result = $wpdb->insert(
            $this->getTableName(),
            $data,
            $this->getFormats($data)
        );

        if ($result === false) {
            $this->log()->error('nfe.queue.add_failed', [
                'data' => $data,
                'error' => $wpdb->last_error,
            ]);
            return false;
        }

        $queue_id = $wpdb->insert_id;
        
        $this->log()->info('nfe.queue.task_added', [
            'queue_id' => $queue_id,
            'order_id' => $data['order_id'] ?? null,
            'task_type' => $data['task_type'] ?? null,
        ]);

        return $queue_id;
    }

    /**
     * Update queue task
     *
     * @param int $id Queue ID
     * @param array $data Data to update
     * @return bool True on success, false on failure
     */
    public function update(int $id, array $data): bool
    {
        global $wpdb;

        $result = $wpdb->update(
            $this->getTableName(),
            $data,
            ['id' => $id],
            $this->getFormats($data),
            ['%d']
        );

        if ($result === false) {
            $this->log()->error('nfe.queue.update_failed', [
                'queue_id' => $id,
                'data' => $data,
                'error' => $wpdb->last_error,
            ]);
            return false;
        }

        $this->log()->debug('nfe.queue.task_updated', [
            'queue_id' => $id,
            'data' => $data,
        ]);

        return true;
    }

    /**
     * Get queue task by ID
     *
     * @param int $id Queue ID
     * @return array|null Queue task data or null if not found
     */
    public function findById(int $id): ?array
    {
        global $wpdb;

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} WHERE id = %d",
                $id
            ),
            ARRAY_A
        );

        return $row ?: null;
    }

    /**
     * Get pending tasks
     *
     * @param int $limit Maximum number of tasks
     * @return array List of pending tasks
     */
    public function getPendingTasks(int $limit = 10): array
    {
        global $wpdb;

        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} 
                 WHERE status = 'pending' 
                 AND (scheduled_at IS NULL OR scheduled_at <= NOW())
                 ORDER BY priority ASC, created_at ASC 
                 LIMIT %d",
                $limit
            ),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Get pending tasks by type
     *
     * @param string $taskType Task type
     * @param int $limit Maximum number of tasks
     * @return array List of pending tasks
     */
    public function getPendingTasksByType(string $taskType, int $limit = 10): array
    {
        global $wpdb;

        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} 
                 WHERE status = 'pending' 
                 AND task_type = %s
                 AND (scheduled_at IS NULL OR scheduled_at <= NOW())
                 ORDER BY priority ASC, created_at ASC 
                 LIMIT %d",
                $taskType,
                $limit
            ),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Get tasks by order ID
     *
     * @param int $orderId Order ID
     * @return array List of tasks for order
     */
    public function getTasksByOrderId(int $orderId): array
    {
        global $wpdb;

        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} 
                 WHERE order_id = %d 
                 ORDER BY created_at DESC",
                $orderId
            ),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Get tasks by status
     *
     * @param string $status Task status
     * @param int $limit Maximum number of tasks
     * @param int $offset Offset for pagination
     * @return array List of tasks
     */
    public function getTasksByStatus(string $status, int $limit = 50, int $offset = 0): array
    {
        global $wpdb;

        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} 
                 WHERE status = %s 
                 ORDER BY created_at DESC 
                 LIMIT %d OFFSET %d",
                $status,
                $limit,
                $offset
            ),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Get failed tasks for retry
     *
     * @param int $limit Maximum number of tasks
     * @return array List of failed tasks that can be retried
     */
    public function getFailedTasksForRetry(int $limit = 10): array
    {
        global $wpdb;

        $results = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM {$this->getTableName()} 
                 WHERE status = 'failed' 
                 AND attempts < max_attempts 
                 AND (last_retry_at IS NULL OR last_retry_at < DATE_SUB(NOW(), INTERVAL 5 MINUTE))
                 ORDER BY priority ASC, created_at ASC 
                 LIMIT %d",
                $limit
            ),
            ARRAY_A
        );

        return $results ?: [];
    }

    /**
     * Mark task as processing
     *
     * @param int $id Queue ID
     * @return bool True on success, false on failure
     */
    public function markAsProcessing(int $id): bool
    {
        return $this->update($id, [
            'status' => 'processing',
            'started_at' => current_time('mysql'),
        ]);
    }

    /**
     * Mark task as completed
     *
     * @param int $id Queue ID
     * @param array $result Task result data
     * @return bool True on success, false on failure
     */
    public function markAsCompleted(int $id, array $result = []): bool
    {
        $data = [
            'status' => 'completed',
            'completed_at' => current_time('mysql'),
            'result' => json_encode($result),
        ];

        return $this->update($id, $data);
    }

    /**
     * Mark task as failed
     *
     * @param int $id Queue ID
     * @param string $errorMessage Error message
     * @return bool True on success, false on failure
     */
    public function markAsFailed(int $id, string $errorMessage): bool
    {
        return $this->update($id, [
            'error_message' => $errorMessage,
            'last_retry_at' => current_time('mysql'),
        ]);
    }

    /**
     * Increment task attempts
     *
     * @param int $id Queue ID
     * @return bool True on success, false on failure
     */
    public function incrementAttempts(int $id): bool
    {
        global $wpdb;

        $result = $wpdb->query(
            $wpdb->prepare(
                "UPDATE {$this->getTableName()} 
                 SET attempts = attempts + 1, last_retry_at = NOW() 
                 WHERE id = %d",
                $id
            )
        );

        if ($result === false) {
            $this->log()->error('nfe.queue.increment_attempts_failed', [
                'queue_id' => $id,
                'error' => $wpdb->last_error,
            ]);
            return false;
        }

        return true;
    }

    /**
     * Delete task from queue
     *
     * @param int $id Queue ID
     * @return bool True on success, false on failure
     */
    public function delete(int $id): bool
    {
        global $wpdb;

        $result = $wpdb->delete(
            $this->getTableName(),
            ['id' => $id],
            ['%d']
        );

        if ($result === false) {
            $this->log()->error('nfe.queue.delete_failed', [
                'queue_id' => $id,
                'error' => $wpdb->last_error,
            ]);
            return false;
        }

        $this->log()->info('nfe.queue.task_deleted', [
            'queue_id' => $id,
        ]);

        return true;
    }

    /**
     * Delete completed tasks older than specified days
     *
     * @param int $daysOld Delete tasks older than this many days
     * @return int Number of tasks deleted
     */
    public function deleteOldCompletedTasks(int $daysOld = 7): int
    {
        global $wpdb;

        $result = $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM {$this->getTableName()} 
                 WHERE status = 'completed' 
                 AND completed_at < DATE_SUB(NOW(), INTERVAL %d DAY)",
                $daysOld
            )
        );

        if ($result === false) {
            $this->log()->error('nfe.queue.delete_old_failed', [
                'days_old' => $daysOld,
                'error' => $wpdb->last_error,
            ]);
            return 0;
        }

        $this->log()->info('nfe.queue.old_tasks_deleted', [
            'days_old' => $daysOld,
            'count' => $result,
        ]);

        return $result;
    }

    /**
     * Count tasks by status
     *
     * @param string $status Task status
     * @return int Number of tasks
     */
    public function countByStatus(string $status): int
    {
        global $wpdb;

        return (int) $wpdb->get_var(
            $wpdb->prepare(
                "SELECT COUNT(*) FROM {$this->getTableName()} WHERE status = %s",
                $status
            )
        );
    }

    /**
     * Count all tasks
     *
     * @return int Total number of tasks
     */
    public function countAll(): int
    {
        global $wpdb;

        return (int) $wpdb->get_var(
            "SELECT COUNT(*) FROM {$this->getTableName()}"
        );
    }

    /**
     * Get queue statistics
     *
     * @return array Queue statistics
     */
    public function getStatistics(): array
    {
        global $wpdb;

        $stats = $wpdb->get_row(
            "SELECT 
                COUNT(*) as total,
                COUNT(CASE WHEN status = 'pending' THEN 1 END) as pending,
                COUNT(CASE WHEN status = 'processing' THEN 1 END) as processing,
                COUNT(CASE WHEN status = 'completed' THEN 1 END) as completed,
                COUNT(CASE WHEN status = 'failed' THEN 1 END) as failed,
                AVG(CASE WHEN status = 'completed' 
                    THEN TIMESTAMPDIFF(SECOND, started_at, completed_at) 
                    END) as avg_processing_time_seconds
            FROM {$this->getTableName()}",
            ARRAY_A
        );

        return $stats ?: [];
    }

    /**
     * Get format placeholders for data
     *
     * @param array $data Data array
     * @return array Format placeholders
     */
    private function getFormats(array $data): array
    {
        $formats = [];
        
        foreach ($data as $key => $value) {
            if (is_int($value)) {
                $formats[] = '%d';
            } elseif (is_float($value)) {
                $formats[] = '%f';
            } else {
                $formats[] = '%s';
            }
        }
        
        return $formats;
    }
}
