<?php
declare(strict_types=1);

namespace App\Modules\Shipments;

use PDO;

final class DeliveryStatusRepository
{
    private static ?array $cache = null;

    public function __construct(private readonly PDO $db)
    {
    }

    public function getAll(): array
    {
        if (self::$cache !== null) {
            return self::$cache;
        }

        $statement = $this->db->prepare(
            'SELECT * FROM delivery_statuses ORDER BY sort_order ASC'
        );
        $statement->execute();
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        self::$cache = is_array($rows) ? $rows : [];

        return self::$cache;
    }

    public function getByKey(string $key): ?array
    {
        foreach ($this->getAll() as $row) {
            if ($row['key'] === $key) {
                return $row;
            }
        }

        return null;
    }

    public function getAllAsOptions(): array
    {
        $options = [];
        foreach ($this->getAll() as $row) {
            $options[$row['key']] = $row['label_pl'];
        }

        return $options;
    }

    public function create(array $data): int
    {
        if ($this->getByKey($data['key']) !== null) {
            throw new \RuntimeException('Status o tym kluczu już istnieje.');
        }

        $statement = $this->db->prepare(
            'INSERT INTO delivery_statuses (`key`, label_pl, color, sort_order, is_terminal)
             VALUES (:key, :label_pl, :color, :sort_order, :is_terminal)'
        );
        $statement->bindValue(':key', $data['key']);
        $statement->bindValue(':label_pl', $data['label_pl']);
        $statement->bindValue(':color', $data['color']);
        $statement->bindValue(':sort_order', $data['sort_order'], PDO::PARAM_INT);
        $statement->bindValue(':is_terminal', $data['is_terminal'], PDO::PARAM_INT);
        $statement->execute();

        $this->clearCache();

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

    public function update(int $id, array $data): void
    {
        $row = $this->findById($id);
        if ($row === null) {
            throw new \RuntimeException('Status nie istnieje.');
        }

        if ((int) $row['is_system'] === 1) {
            throw new \RuntimeException('Statusów systemowych nie można edytować.');
        }

        $statement = $this->db->prepare(
            'UPDATE delivery_statuses
             SET label_pl = :label_pl, color = :color, sort_order = :sort_order, is_terminal = :is_terminal
             WHERE id = :id'
        );
        $statement->bindValue(':label_pl', $data['label_pl']);
        $statement->bindValue(':color', $data['color']);
        $statement->bindValue(':sort_order', $data['sort_order'], PDO::PARAM_INT);
        $statement->bindValue(':is_terminal', $data['is_terminal'], PDO::PARAM_INT);
        $statement->bindValue(':id', $id, PDO::PARAM_INT);
        $statement->execute();

        $this->clearCache();
    }

    public function delete(int $id): void
    {
        $row = $this->findById($id);
        if ($row === null) {
            throw new \RuntimeException('Status nie istnieje.');
        }

        if ((int) $row['is_system'] === 1) {
            throw new \RuntimeException('Statusów systemowych nie można usunąć.');
        }

        $key = (string) $row['key'];

        $stmtMappings = $this->db->prepare(
            'SELECT COUNT(*) FROM delivery_status_mappings WHERE normalized_status = :key'
        );
        $stmtMappings->bindValue(':key', $key);
        $stmtMappings->execute();
        $countMappings = (int) $stmtMappings->fetchColumn();

        $stmtPackages = $this->db->prepare(
            'SELECT COUNT(*) FROM shipment_packages WHERE delivery_status = :key'
        );
        $stmtPackages->bindValue(':key', $key);
        $stmtPackages->execute();
        $countPackages = (int) $stmtPackages->fetchColumn();

        if ($countMappings > 0 || $countPackages > 0) {
            throw new \RuntimeException('Status jest używany i nie może być usunięty.');
        }

        $statement = $this->db->prepare(
            'DELETE FROM delivery_statuses WHERE id = :id'
        );
        $statement->bindValue(':id', $id, PDO::PARAM_INT);
        $statement->execute();

        $this->clearCache();
    }

    public function clearCache(): void
    {
        self::$cache = null;
    }

    private function findById(int $id): ?array
    {
        foreach ($this->getAll() as $row) {
            if ((int) $row['id'] === $id) {
                return $row;
            }
        }

        return null;
    }
}
