<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use App\Core\Exceptions\IntegrationConfigException;
use App\Core\Support\StringHelper;
use PDO;
use Throwable;

final class PolkurierIntegrationRepository
{
    private const INTEGRATION_TYPE = 'polkurier';
    private const INTEGRATION_NAME = 'polkurier';
    private const INTEGRATION_BASE_URL = 'https://api.polkurier.pl/';

    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
    {
        $this->ensureRow();
        $integrationId = $this->ensureBaseIntegration();
        $row = $this->fetchRow();
        $integration = $this->integrations->findById($integrationId);
        $tokenEncrypted = $this->resolveTokenEncrypted($row, $integration);

        return [
            'integration_id' => $integrationId,
            'login' => trim((string) ($row['login'] ?? '')),
            'default_label_format' => trim((string) ($row['default_label_format'] ?? 'PDF')) ?: 'PDF',
            'has_api_token' => $tokenEncrypted !== null && $tokenEncrypted !== '',
            'is_active' => (int) ($integration['is_active'] ?? 1) === 1,
            'last_test_status' => trim((string) ($integration['last_test_status'] ?? '')),
            'last_test_http_code' => isset($integration['last_test_http_code']) ? (int) $integration['last_test_http_code'] : null,
            'last_test_message' => trim((string) ($integration['last_test_message'] ?? '')),
            'last_test_at' => trim((string) ($integration['last_test_at'] ?? '')),
            'updated_at' => trim((string) ($row['updated_at'] ?? '')),
        ];
    }

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

        $login = trim((string) ($payload['login'] ?? ''));
        if ($login === '' || strlen($login) > 190) {
            throw new IntegrationConfigException('Podaj login polkurier (e-mail lub identyfikator z panelu klienta, maks. 190 znakow).');
        }

        $labelFormatRaw = strtoupper(trim((string) ($payload['default_label_format'] ?? 'PDF')));
        $allowedFormats = ['PDF', 'ZPL', 'EPL'];
        $labelFormat = in_array($labelFormatRaw, $allowedFormats, true) ? $labelFormatRaw : 'PDF';

        $currentEncrypted = $this->resolveTokenEncrypted($row, $this->integrations->findById($integrationId));
        $token = trim((string) ($payload['api_token'] ?? ''));
        $nextEncrypted = $currentEncrypted;
        if ($token !== '') {
            $nextEncrypted = $this->cipher->encrypt($token);
        }

        if ($nextEncrypted === null || $nextEncrypted === '') {
            throw new IntegrationConfigException('Podaj Token API polkurier (z Panel Klienta -> Ustawienia -> Token API).');
        }

        $statement = $this->pdo->prepare(
            'UPDATE polkurier_integration_settings
             SET login = :login,
                 api_token_encrypted = :api_token_encrypted,
                 default_label_format = :default_label_format,
                 updated_at = NOW()
             WHERE id = 1'
        );
        $statement->execute([
            'login' => $login,
            'api_token_encrypted' => $nextEncrypted,
            'default_label_format' => $labelFormat,
        ]);

        $this->updateIntegrationActive($integrationId, !empty($payload['is_active']));
        $this->integrations->updateApiKeyEncrypted($integrationId, $nextEncrypted);
    }

    /**
     * @return array{integration_id: int, login: string, api_token: string, default_label_format: string}|null
     */
    public function getCredentials(): ?array
    {
        $this->ensureRow();
        $integrationId = $this->ensureBaseIntegration();
        $row = $this->fetchRow();
        if ($row === null) {
            return null;
        }

        $integration = $this->integrations->findById($integrationId);
        if (empty($integration) || (int) ($integration['is_active'] ?? 0) !== 1) {
            return null;
        }

        $login = trim((string) ($row['login'] ?? ''));
        $encrypted = $this->resolveTokenEncrypted($row, $integration);

        if ($login === '' || $encrypted === null || $encrypted === '') {
            return null;
        }

        $token = trim((string) $this->cipher->decrypt($encrypted));
        if ($token === '') {
            return null;
        }

        return [
            'integration_id' => $integrationId,
            'login' => $login,
            'api_token' => $token,
            'default_label_format' => trim((string) ($row['default_label_format'] ?? 'PDF')) ?: 'PDF',
        ];
    }

    public function getIntegrationId(): ?int
    {
        $integration = $this->integrations->findByTypeAndName(self::INTEGRATION_TYPE, self::INTEGRATION_NAME);
        if ($integration === null) {
            return null;
        }

        $id = (int) ($integration['id'] ?? 0);
        return $id > 0 ? $id : null;
    }

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

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

    /**
     * @return array<string, mixed>|null
     */
    private function fetchRow(): ?array
    {
        try {
            $statement = $this->pdo->prepare('SELECT * FROM polkurier_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;
    }

    /**
     * @param array<string, mixed>|null $row
     * @param array<string, mixed>|null $integration
     */
    private function resolveTokenEncrypted(?array $row, ?array $integration): ?string
    {
        $settingsValue = trim((string) ($row['api_token_encrypted'] ?? ''));
        if ($settingsValue !== '') {
            return $settingsValue;
        }

        $baseValue = trim((string) ($integration['api_key_encrypted'] ?? ''));
        return StringHelper::nullableString($baseValue);
    }

    private function updateIntegrationActive(int $integrationId, bool $isActive): void
    {
        $statement = $this->pdo->prepare(
            'UPDATE integrations
             SET is_active = :is_active,
                 updated_at = NOW()
             WHERE id = :id AND type = :type'
        );
        $statement->execute([
            'id' => $integrationId,
            'type' => self::INTEGRATION_TYPE,
            'is_active' => $isActive ? 1 : 0,
        ]);
    }
}
