<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use PDO;
use Throwable;

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

    /**
     * @return array<int, array<string, mixed>>
     */
    public function listMappings(string $sourceSystem, int $sourceIntegrationId = 0): array
    {
        $stmt = $this->pdo->prepare(
            'SELECT *
             FROM carrier_delivery_method_mappings
             WHERE source_system = :source_system
               AND source_integration_id = :source_integration_id
             ORDER BY order_delivery_method ASC'
        );
        $stmt->execute([
            'source_system' => $this->normalizeSourceSystem($sourceSystem),
            'source_integration_id' => max(0, $sourceIntegrationId),
        ]);

        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<string, mixed>|null
     */
    public function findByOrderMethod(string $sourceSystem, int $sourceIntegrationId, string $orderDeliveryMethod): ?array
    {
        $method = trim($orderDeliveryMethod);
        if ($method === '') {
            return null;
        }

        $stmt = $this->pdo->prepare(
            'SELECT *
             FROM carrier_delivery_method_mappings
             WHERE source_system = :source_system
               AND source_integration_id = :source_integration_id
               AND order_delivery_method = :order_delivery_method
             LIMIT 1'
        );
        $stmt->execute([
            'source_system' => $this->normalizeSourceSystem($sourceSystem),
            'source_integration_id' => max(0, $sourceIntegrationId),
            'order_delivery_method' => $method,
        ]);

        $row = $stmt->fetch(PDO::FETCH_ASSOC);
        return is_array($row) ? $row : null;
    }

    public function hasMappingsForSource(string $sourceSystem, int $sourceIntegrationId = 0): bool
    {
        $stmt = $this->pdo->prepare(
            'SELECT 1
             FROM carrier_delivery_method_mappings
             WHERE source_system = :source_system
               AND source_integration_id = :source_integration_id
             LIMIT 1'
        );
        $stmt->execute([
            'source_system' => $this->normalizeSourceSystem($sourceSystem),
            'source_integration_id' => max(0, $sourceIntegrationId),
        ]);

        $value = $stmt->fetchColumn();
        return $value !== false;
    }

    /**
     * @param array<int, array<string, string>> $mappings
     */
    public function saveMappings(string $sourceSystem, int $sourceIntegrationId, array $mappings): void
    {

        $normalizedSource = $this->normalizeSourceSystem($sourceSystem);
        $normalizedIntegrationId = max(0, $sourceIntegrationId);
        $this->pdo->beginTransaction();
        try {
            $deleteStmt = $this->pdo->prepare(
                'DELETE FROM carrier_delivery_method_mappings
                 WHERE source_system = :source_system
                   AND source_integration_id = :source_integration_id'
            );
            $deleteStmt->execute([
                'source_system' => $normalizedSource,
                'source_integration_id' => $normalizedIntegrationId,
            ]);

            if ($mappings !== []) {
                $insertStmt = $this->pdo->prepare(
                    'INSERT INTO carrier_delivery_method_mappings (
                        source_system,
                        source_integration_id,
                        order_delivery_method,
                        provider,
                        provider_service_id,
                        provider_account_id,
                        provider_carrier_id,
                        provider_service_name
                    ) VALUES (
                        :source_system,
                        :source_integration_id,
                        :order_delivery_method,
                        :provider,
                        :provider_service_id,
                        :provider_account_id,
                        :provider_carrier_id,
                        :provider_service_name
                    )'
                );

                foreach ($mappings as $mapping) {
                    $orderMethod = $this->limit(trim((string) ($mapping['order_delivery_method'] ?? '')), 200);
                    $provider = $this->limit(strtolower(trim((string) ($mapping['provider'] ?? ''))), 50);
                    $providerServiceId = $this->limit(trim((string) ($mapping['provider_service_id'] ?? '')), 128);

                    if ($orderMethod === '' || $provider === '' || $providerServiceId === '') {
                        continue;
                    }

                    $insertStmt->execute([
                        'source_system' => $normalizedSource,
                        'source_integration_id' => $normalizedIntegrationId,
                        'order_delivery_method' => $orderMethod,
                        'provider' => $provider,
                        'provider_service_id' => $providerServiceId,
                        'provider_account_id' => $this->nullableLimited((string) ($mapping['provider_account_id'] ?? ''), 128),
                        'provider_carrier_id' => $this->nullableLimited((string) ($mapping['provider_carrier_id'] ?? ''), 128),
                        'provider_service_name' => $this->nullableLimited((string) ($mapping['provider_service_name'] ?? ''), 255),
                    ]);
                }
            }
            $this->pdo->commit();
        } catch (Throwable $exception) {
            if ($this->pdo->inTransaction()) {
                $this->pdo->rollBack();
            }
            throw $exception;
        }
    }

    /**
     * @return array<int, string>
     */
    public function getDistinctOrderDeliveryMethods(string $sourceSystem, int $sourceIntegrationId = 0): array
    {
        $normalizedSource = $this->normalizeSourceSystem($sourceSystem);

        if ($normalizedSource === 'shoppro') {
            $stmt = $this->pdo->prepare(
                "SELECT DISTINCT external_carrier_id
                 FROM orders
                 WHERE external_carrier_id IS NOT NULL
                   AND external_carrier_id <> ''
                   AND source = 'shoppro'
                   AND integration_id = :integration_id
                 ORDER BY external_carrier_id ASC"
            );
            $stmt->execute(['integration_id' => max(0, $sourceIntegrationId)]);
            $rows = $stmt->fetchAll(PDO::FETCH_COLUMN);
            return is_array($rows) ? $rows : [];
        }

        $stmt = $this->pdo->query(
            "SELECT DISTINCT external_carrier_id
             FROM orders
             WHERE external_carrier_id IS NOT NULL
               AND external_carrier_id <> ''
               AND source = 'allegro'
               AND external_carrier_id NOT REGEXP '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'
             ORDER BY external_carrier_id ASC"
        );

        $rows = $stmt !== false ? $stmt->fetchAll(PDO::FETCH_COLUMN) : [];
        return is_array($rows) ? $rows : [];
    }

    private function normalizeSourceSystem(string $value): string
    {
        $source = strtolower(trim($value));
        return in_array($source, ['allegro', 'shoppro'], true) ? $source : 'allegro';
    }

    private function nullableLimited(string $value, int $max): ?string
    {
        $trimmed = $this->limit(trim($value), $max);
        return $trimmed === '' ? null : $trimmed;
    }

    private function limit(string $value, int $max): string
    {
        if ($max <= 0) {
            return '';
        }
        if (mb_strlen($value) <= $max) {
            return $value;
        }
        return mb_substr($value, 0, $max);
    }
}
