<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Support\StringHelper;
use PDO;
use AppCorexceptionsIntegrationConfigException;
use Throwable;

final class InpostIntegrationRepository
{
    private const INTEGRATION_TYPE = 'inpost';
    private const INTEGRATION_NAME = 'InPost ShipX';
    private const INTEGRATION_BASE_URL = 'https://api-shipx-pl.easypack24.net';

    private readonly IntegrationsRepository $integrations;
    private readonly IntegrationSecretCipher $cipher;

    public function __construct(
        private readonly PDO $pdo,
        private readonly string $secret
    ) {
        $this->integrations = new IntegrationsRepository($this->pdo);
        $this->cipher = new IntegrationSecretCipher($this->secret);
    }

    /**
     * @return array<string, mixed>
     */
    public function getSettings(): array
    {
        $row = $this->fetchRow();
        if ($row === null) {
            $row = $this->defaultSettings();
        }

        $encryptedToken = $this->resolveEncryptedToken($row);

        return [
            'has_api_token' => $encryptedToken !== null && $encryptedToken !== '',
            'organization_id' => (string) ($row['organization_id'] ?? ''),
            'environment' => (string) ($row['environment'] ?? 'sandbox'),
            'default_dispatch_method' => (string) ($row['default_dispatch_method'] ?? 'pop'),
            'default_dispatch_point' => (string) ($row['default_dispatch_point'] ?? ''),
            'default_insurance' => isset($row['default_insurance']) && $row['default_insurance'] !== null
                ? (float) $row['default_insurance']
                : null,
            'default_locker_size' => (string) ($row['default_locker_size'] ?? 'small'),
            'default_courier_length' => (int) ($row['default_courier_length'] ?? 20),
            'default_courier_width' => (int) ($row['default_courier_width'] ?? 15),
            'default_courier_height' => (int) ($row['default_courier_height'] ?? 8),
            'label_format' => (string) ($row['label_format'] ?? 'Pdf'),
            'weekend_delivery' => (bool) ($row['weekend_delivery'] ?? false),
            'auto_insurance_value' => (bool) ($row['auto_insurance_value'] ?? false),
            'multi_parcel' => (bool) ($row['multi_parcel'] ?? false),
        ];
    }

    /**
     * @param array<string, mixed> $payload
     */
    public function saveSettings(array $payload): void
    {
        $this->ensureRow();
        $current = $this->fetchRow();
        if ($current === null) {
            throw new IntegrationConfigException('Brak rekordu konfiguracji InPost.');
        }

        $integrationId = $this->ensureBaseIntegration();
        $currentEncrypted = $this->resolveEncryptedToken($current);

        $apiToken = trim((string) ($payload['api_token'] ?? ''));
        $nextEncrypted = $currentEncrypted;
        if ($apiToken !== '') {
            $nextEncrypted = $this->cipher->encrypt($apiToken);
        }

        $this->integrations->updateApiKeyEncrypted($integrationId, $nextEncrypted);

        $statement = $this->pdo->prepare(
            'UPDATE inpost_integration_settings
            SET api_token_encrypted = :api_token_encrypted,
                organization_id = :organization_id,
                environment = :environment,
                default_dispatch_method = :default_dispatch_method,
                default_dispatch_point = :default_dispatch_point,
                default_insurance = :default_insurance,
                default_locker_size = :default_locker_size,
                default_courier_length = :default_courier_length,
                default_courier_width = :default_courier_width,
                default_courier_height = :default_courier_height,
                label_format = :label_format,
                weekend_delivery = :weekend_delivery,
                auto_insurance_value = :auto_insurance_value,
                multi_parcel = :multi_parcel,
                updated_at = NOW()
            WHERE id = 1'
        );
        $statement->execute([
            'api_token_encrypted' => StringHelper::nullableString((string) $nextEncrypted),
            'organization_id' => StringHelper::nullableString(trim((string) ($payload['organization_id'] ?? ''))),
            'environment' => in_array($payload['environment'] ?? '', ['sandbox', 'production'], true)
                ? $payload['environment']
                : 'sandbox',
            'default_dispatch_method' => in_array($payload['default_dispatch_method'] ?? '', ['pop', 'parcel_locker', 'courier'], true)
                ? $payload['default_dispatch_method']
                : 'pop',
            'default_dispatch_point' => StringHelper::nullableString(trim((string) ($payload['default_dispatch_point'] ?? ''))),
            'default_insurance' => ($payload['default_insurance'] ?? '') !== ''
                ? (float) $payload['default_insurance']
                : null,
            'default_locker_size' => in_array($payload['default_locker_size'] ?? '', ['small', 'medium', 'large'], true)
                ? $payload['default_locker_size']
                : 'small',
            'default_courier_length' => max(1, (int) ($payload['default_courier_length'] ?? 20)),
            'default_courier_width' => max(1, (int) ($payload['default_courier_width'] ?? 15)),
            'default_courier_height' => max(1, (int) ($payload['default_courier_height'] ?? 8)),
            'label_format' => in_array($payload['label_format'] ?? '', ['Pdf', 'Zpl', 'Epl'], true)
                ? $payload['label_format']
                : 'Pdf',
            'weekend_delivery' => !empty($payload['weekend_delivery']) ? 1 : 0,
            'auto_insurance_value' => !empty($payload['auto_insurance_value']) ? 1 : 0,
            'multi_parcel' => !empty($payload['multi_parcel']) ? 1 : 0,
        ]);
    }

    /**
     * @return string|null Decrypted API token or null
     */
    public function getDecryptedToken(): ?string
    {
        $row = $this->fetchRow();
        if ($row === null) {
            return null;
        }

        $encrypted = $this->resolveEncryptedToken($row);
        if ($encrypted === null || $encrypted === '') {
            return null;
        }

        return $this->cipher->decrypt($encrypted);
    }

    private function ensureRow(): void
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO inpost_integration_settings (id, created_at, updated_at)
            VALUES (1, NOW(), NOW())
            ON DUPLICATE KEY UPDATE updated_at = VALUES(updated_at)'
        );
        $statement->execute();
    }

    /**
     * @return array<string, mixed>|null
     */
    private function fetchRow(): ?array
    {
        try {
            $statement = $this->pdo->prepare('SELECT * FROM inpost_integration_settings WHERE id = 1 LIMIT 1');
            $statement->execute();
            $row = $statement->fetch(PDO::FETCH_ASSOC);
        } catch (Throwable) {
            return null;
        }

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

    /**
     * @return array<string, mixed>
     */
    private function defaultSettings(): array
    {
        return [
            'has_api_token' => false,
            'organization_id' => '',
            'environment' => 'sandbox',
            'default_dispatch_method' => 'pop',
            'default_dispatch_point' => '',
            'default_insurance' => null,
            'default_locker_size' => 'small',
            'default_courier_length' => 20,
            'default_courier_width' => 15,
            'default_courier_height' => 8,
            'label_format' => 'Pdf',
            'weekend_delivery' => false,
            'auto_insurance_value' => false,
            'multi_parcel' => false,
        ];
    }

    private function resolveEncryptedToken(array $row): ?string
    {
        $integrationId = $this->ensureBaseIntegration();
        $fromBase = $this->integrations->getApiKeyEncrypted($integrationId);
        if ($fromBase !== null && $fromBase !== '') {
            return $fromBase;
        }

        $legacy = trim((string) ($row['api_token_encrypted'] ?? ''));
        return $legacy === '' ? null : $legacy;
    }

    private function ensureBaseIntegration(): int
    {
        return $this->integrations->ensureIntegration(
            self::INTEGRATION_TYPE,
            self::INTEGRATION_NAME,
            self::INTEGRATION_BASE_URL,
            20,
            true
        );
    }

}
