<?php
declare(strict_types=1);

namespace App\Modules\Printing;

use PDO;

final class PrintJobRepository
{
    public function __construct(
        private readonly PDO $pdo
    ) {
    }

    /**
     * @param array<string, mixed> $data
     */
    public function create(array $data): int
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO print_jobs (order_id, package_id, label_path, created_by)
             VALUES (:order_id, :package_id, :label_path, :created_by)'
        );
        $statement->execute([
            'order_id' => $data['order_id'],
            'package_id' => $data['package_id'],
            'label_path' => $data['label_path'],
            'created_by' => $data['created_by'],
        ]);

        return (int) $this->pdo->lastInsertId();
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function findPending(): array
    {
        $statement = $this->pdo->query(
            'SELECT pj.id, pj.order_id, pj.package_id, pj.label_path, pj.created_at,
                    o.internal_order_number AS order_number,
                    sp.tracking_number
             FROM print_jobs pj
             LEFT JOIN orders o ON o.id = pj.order_id
             LEFT JOIN shipment_packages sp ON sp.id = pj.package_id
             WHERE pj.status = \'pending\'
             ORDER BY pj.created_at ASC'
        );
        $rows = $statement !== false ? $statement->fetchAll(PDO::FETCH_ASSOC) : [];

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findById(int $id): ?array
    {
        $statement = $this->pdo->prepare(
            'SELECT pj.*, o.internal_order_number AS order_number, sp.tracking_number
             FROM print_jobs pj
             LEFT JOIN orders o ON o.id = pj.order_id
             LEFT JOIN shipment_packages sp ON sp.id = pj.package_id
             WHERE pj.id = :id
             LIMIT 1'
        );
        $statement->execute(['id' => $id]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);

        return is_array($row) ? $row : null;
    }

    public function markCompleted(int $id): void
    {
        $statement = $this->pdo->prepare(
            'UPDATE print_jobs SET status = \'completed\', completed_at = NOW() WHERE id = :id'
        );
        $statement->execute(['id' => $id]);
    }

    public function markFailed(int $id): void
    {
        $statement = $this->pdo->prepare(
            'UPDATE print_jobs SET status = \'failed\' WHERE id = :id'
        );
        $statement->execute(['id' => $id]);
    }

    public function deleteById(int $id): bool
    {
        $statement = $this->pdo->prepare('DELETE FROM print_jobs WHERE id = :id');
        $statement->execute(['id' => $id]);

        return $statement->rowCount() > 0;
    }

    /**
     * @return list<int>
     */
    public function pendingPackageIds(): array
    {
        $statement = $this->pdo->query(
            'SELECT DISTINCT package_id FROM print_jobs WHERE status = \'pending\''
        );
        $rows = $statement !== false ? $statement->fetchAll(PDO::FETCH_COLUMN) : [];

        return is_array($rows) ? array_map('intval', $rows) : [];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findPendingByPackageId(int $packageId): ?array
    {
        $statement = $this->pdo->prepare(
            'SELECT id FROM print_jobs WHERE package_id = :package_id AND status = \'pending\' LIMIT 1'
        );
        $statement->execute(['package_id' => $packageId]);
        $row = $statement->fetch(PDO::FETCH_ASSOC);

        return is_array($row) ? $row : null;
    }

    /**
     * @return list<array<string, mixed>>
     */
    public function getRecentJobs(int $limit = 50, ?string $statusFilter = null): array
    {
        $sql = 'SELECT pj.id, pj.order_id, pj.package_id, pj.label_path, pj.status,
                       pj.created_at, pj.completed_at,
                       o.internal_order_number AS order_number,
                       sp.tracking_number
                FROM print_jobs pj
                LEFT JOIN orders o ON o.id = pj.order_id
                LEFT JOIN shipment_packages sp ON sp.id = pj.package_id';

        $params = [];
        if ($statusFilter !== null && $statusFilter !== '') {
            $sql .= ' WHERE pj.status = :status';
            $params['status'] = $statusFilter;
        }

        $sql .= ' ORDER BY pj.created_at DESC LIMIT ' . max(1, $limit);

        $statement = $this->pdo->prepare($sql);
        $statement->execute($params);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

}
