<?php
declare(strict_types=1);

namespace App\Modules\Settings;

use PDO;
use AppCorexceptionsIntegrationConfigException;
use Throwable;

final class ApaczkaIntegrationRepository
{
    private const INTEGRATION_TYPE = 'apaczka';
    private const INTEGRATION_NAME = 'Apaczka';
    private const INTEGRATION_BASE_URL = 'https://www.apaczka.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);

        $appId = trim((string) ($row['app_id'] ?? ''));
        $secretEncrypted = $this->resolveSecretEncrypted($row, $integration);

        return [
            'app_id' => $appId,
            'has_app_id' => $appId !== '',
            'has_app_secret' => $secretEncrypted !== null && $secretEncrypted !== '',
            'has_api_key' => $secretEncrypted !== null && $secretEncrypted !== '',
            'updated_at' => (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 Apaczka.');
        }

        $appId = trim((string) ($payload['app_id'] ?? ''));
        if ($appId === '') {
            throw new IntegrationConfigException('Podaj App ID Apaczka.');
        }

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

        if ($nextEncrypted === null || $nextEncrypted === '') {
            throw new IntegrationConfigException('Podaj App Secret Apaczka.');
        }

        $stmt = $this->pdo->prepare(
            'UPDATE apaczka_integration_settings
             SET app_id = :app_id,
                 app_secret_encrypted = :app_secret_encrypted,
                 api_key_encrypted = :api_key_encrypted,
                 updated_at = NOW()
             WHERE id = 1'
        );
        $stmt->execute([
            'app_id' => $appId,
            'app_secret_encrypted' => $nextEncrypted,
            'api_key_encrypted' => $nextEncrypted,
        ]);

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

    /**
     * @return array{app_id:string, app_secret:string}|null
     */
    public function getApiCredentials(): ?array
    {
        $settings = $this->getSettings();
        $appId = trim((string) ($settings['app_id'] ?? ''));
        if ($appId === '') {
            return null;
        }

        $row = $this->fetchRow();
        $integrationId = $this->ensureBaseIntegration();
        $integration = $this->integrations->findById($integrationId);
        $encrypted = $this->resolveSecretEncrypted($row, $integration);
        if ($encrypted === null || $encrypted === '') {
            return null;
        }

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

        return [
            'app_id' => $appId,
            'app_secret' => $secret,
        ];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function testConnection(ApaczkaApiClient $apiClient): array
    {
        $credentials = $this->getApiCredentials();
        if ($credentials === null) {
            throw new IntegrationConfigException('Brak konfiguracji App ID/App Secret Apaczka.');
        }

        return $apiClient->getServiceStructure(
            (string) $credentials['app_id'],
            (string) $credentials['app_secret']
        );
    }

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

    private function ensureRow(): void
    {
        $integrationId = $this->ensureBaseIntegration();

        $stmt = $this->pdo->prepare(
            'INSERT INTO apaczka_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)'
        );
        $stmt->execute(['integration_id' => $integrationId]);
    }

    /**
     * @return array<string, mixed>|null
     */
    private function fetchRow(): ?array
    {
        try {
            $stmt = $this->pdo->prepare('SELECT * FROM apaczka_integration_settings WHERE id = 1 LIMIT 1');
            $stmt->execute();
            $row = $stmt->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 resolveSecretEncrypted(?array $row, ?array $integration): ?string
    {
        $base = trim((string) ($integration['api_key_encrypted'] ?? ''));
        if ($base !== '') {
            return $base;
        }

        $modern = trim((string) ($row['app_secret_encrypted'] ?? ''));
        if ($modern !== '') {
            return $modern;
        }

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