<?php
declare(strict_types=1);

namespace App\Modules\Shipments;

use PDO;

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

    /**
     * @return array<int, array{provider: string, raw_status: string, normalized_status: string, description: string}>
     */
    public function listByProvider(string $provider): array
    {
        $provider = strtolower(trim($provider));
        if ($provider === '') {
            return [];
        }

        $statement = $this->pdo->prepare(
            'SELECT provider, raw_status, normalized_status, description
            FROM delivery_status_mappings
            WHERE provider = :provider
            ORDER BY raw_status ASC'
        );
        $statement->execute(['provider' => $provider]);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        return array_map(static function (array $row): array {
            return [
                'provider' => (string) ($row['provider'] ?? ''),
                'raw_status' => (string) ($row['raw_status'] ?? ''),
                'normalized_status' => (string) ($row['normalized_status'] ?? ''),
                'description' => (string) ($row['description'] ?? ''),
            ];
        }, $rows);
    }

    public function upsertMapping(string $provider, string $rawStatus, string $normalizedStatus, string $description): void
    {
        $provider = strtolower(trim($provider));
        $rawStatus = trim($rawStatus);
        if ($provider === '' || $rawStatus === '') {
            return;
        }

        $statement = $this->pdo->prepare(
            'INSERT INTO delivery_status_mappings (provider, raw_status, normalized_status, description, created_at, updated_at)
            VALUES (:provider, :raw_status, :normalized_status, :description, NOW(), NOW())
            ON DUPLICATE KEY UPDATE
                normalized_status = VALUES(normalized_status),
                description = VALUES(description),
                updated_at = NOW()'
        );
        $statement->execute([
            'provider' => $provider,
            'raw_status' => $rawStatus,
            'normalized_status' => $normalizedStatus,
            'description' => $description,
        ]);
    }

    public function deleteMapping(string $provider, string $rawStatus): void
    {
        $provider = strtolower(trim($provider));
        $rawStatus = trim($rawStatus);
        if ($provider === '' || $rawStatus === '') {
            return;
        }

        $statement = $this->pdo->prepare(
            'DELETE FROM delivery_status_mappings WHERE provider = :provider AND raw_status = :raw_status'
        );
        $statement->execute([
            'provider' => $provider,
            'raw_status' => $rawStatus,
        ]);
    }

    public function deleteAllByProvider(string $provider): void
    {
        $provider = strtolower(trim($provider));
        if ($provider === '') {
            return;
        }

        $statement = $this->pdo->prepare(
            'DELETE FROM delivery_status_mappings WHERE provider = :provider'
        );
        $statement->execute(['provider' => $provider]);
    }

    /**
     * Niezmapowane raw statusy wykryte w shipment_packages.
     * Zwraca tylko te, ktore nie wystepuja ani w defaultach kodu, ani w overrides DB dla danego providera.
     *
     * @param array<string, array{normalized: string, description: string}> $knownRawStatuses keyed by raw_status (unia defaultow + overrides)
     * @return array<int, array{raw_status: string, count: int, last_seen: ?string}>
     */
    public function listUnmappedRawStatuses(string $provider, array $knownRawStatuses): array
    {
        $provider = strtolower(trim($provider));
        if ($provider === '') {
            return [];
        }

        $statement = $this->pdo->prepare(
            "SELECT delivery_status_raw AS raw_status,
                    COUNT(*) AS cnt,
                    MAX(delivery_status_updated_at) AS last_seen
            FROM shipment_packages
            WHERE provider = :provider
              AND delivery_status_raw IS NOT NULL
              AND delivery_status_raw <> ''
            GROUP BY delivery_status_raw
            ORDER BY cnt DESC, raw_status ASC"
        );
        $statement->execute(['provider' => $provider]);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        $result = [];
        foreach ($rows as $row) {
            $rawStatus = (string) ($row['raw_status'] ?? '');
            if ($rawStatus === '' || isset($knownRawStatuses[$rawStatus])) {
                continue;
            }
            $result[] = [
                'raw_status' => $rawStatus,
                'count' => (int) ($row['cnt'] ?? 0),
                'last_seen' => $row['last_seen'] !== null ? (string) $row['last_seen'] : null,
            ];
        }

        return $result;
    }

    /**
     * Zwraca sumaryczna liczbe niezmapowanych raw statusow dla wszystkich providerow obslugiwanych w UI.
     * Wykorzystywane przez badge w menu. Rezultat cache'owany w pamieci per-request.
     */
    public function countAllUnmappedForBadge(): int
    {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }

        $providers = ['inpost', 'apaczka', 'allegro_wza'];
        $knownKeysByProvider = [];
        foreach ($providers as $prov) {
            $knownKeysByProvider[$prov] = [];
            foreach (DeliveryStatus::getDefaultMappings($prov) as $raw => $_) {
                $knownKeysByProvider[$prov][$raw] = true;
            }
            foreach ($this->listByProvider($prov) as $row) {
                $knownKeysByProvider[$prov][$row['raw_status']] = true;
            }
        }

        $cached = $this->countUnmappedRawStatuses($knownKeysByProvider);
        return $cached;
    }

    /**
     * Sumaryczna liczba niezmapowanych raw statusow we wszystkich providerach.
     * Wykorzystywane przez badge w menu.
     */
    public function countUnmappedRawStatuses(array $knownKeysByProvider): int
    {
        $statement = $this->pdo->query(
            "SELECT provider, delivery_status_raw
            FROM shipment_packages
            WHERE delivery_status_raw IS NOT NULL
              AND delivery_status_raw <> ''
            GROUP BY provider, delivery_status_raw"
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return 0;
        }

        $count = 0;
        foreach ($rows as $row) {
            $provider = (string) ($row['provider'] ?? '');
            $rawStatus = (string) ($row['delivery_status_raw'] ?? '');
            if ($provider === '' || $rawStatus === '') {
                continue;
            }
            $known = $knownKeysByProvider[$provider] ?? [];
            if (!isset($known[$rawStatus])) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * @return array<string, array{normalized_status: string, description: string}>
     */
    public function getAllOverrides(): array
    {
        $statement = $this->pdo->query(
            'SELECT provider, raw_status, normalized_status, description FROM delivery_status_mappings'
        );
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);
        if (!is_array($rows)) {
            return [];
        }

        $result = [];
        foreach ($rows as $row) {
            $key = ((string) ($row['provider'] ?? '')) . ':' . ((string) ($row['raw_status'] ?? ''));
            $result[$key] = [
                'normalized_status' => (string) ($row['normalized_status'] ?? ''),
                'description' => (string) ($row['description'] ?? ''),
            ];
        }

        return $result;
    }
}
