<?php

namespace MintyPHP\Module\Helpdesk\Service;

use MintyPHP\Module\Helpdesk\Repository\HelpdeskTenantSettingsRepository;
use MintyPHP\Service\Settings\SettingsCryptoGatewayInterface;

/**
 * Gateway for per-tenant helpdesk BC connection settings.
 *
 * Wraps the tenant settings repository and handles encryption/decryption
 * of secrets (basic_password, oauth_client_secret) via SettingsCryptoGateway.
 *
 * Uses a per-request row cache to avoid N+1 queries when multiple getters
 * are called for the same tenant (e.g. during settings resolution).
 */
class HelpdeskTenantSettingsGateway
{
    /** @var array<int, array<string, mixed>|false> Cache: tenantId → row or false (not found) */
    private array $rowCache = [];

    public function __construct(
        private readonly HelpdeskTenantSettingsRepository $repository,
        private readonly SettingsCryptoGatewayInterface $settingsCryptoGateway
    ) {
    }

    public function isOverrideEnabled(int $tenantId): bool
    {
        $row = $this->resolveRow($tenantId);
        if ($row === null) {
            return false;
        }

        return (int) ($row['override_enabled'] ?? 0) === 1;
    }

    /**
     * @return array<string, mixed>|null Raw row data (secrets still encrypted)
     */
    public function findByTenantId(int $tenantId): ?array
    {
        return $this->resolveRow($tenantId);
    }

    public function getAuthMode(int $tenantId): string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['auth_mode'] ?? ''));

        return $value === HelpdeskSettingsGateway::AUTH_MODE_OAUTH2
            ? HelpdeskSettingsGateway::AUTH_MODE_OAUTH2
            : HelpdeskSettingsGateway::AUTH_MODE_BASIC;
    }

    public function getODataBaseUrl(int $tenantId): string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['odata_base_url'] ?? ''));

        return $value !== '' ? rtrim($value, '/') : HelpdeskSettingsGateway::DEFAULT_ODATA_BASE_URL;
    }

    public function getCompanyName(int $tenantId): string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['company_name'] ?? ''));

        return $value !== '' ? $value : HelpdeskSettingsGateway::DEFAULT_COMPANY_NAME;
    }

    public function getBasicUser(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['basic_user'] ?? ''));

        return $value !== '' ? $value : null;
    }

    public function getBasicPassword(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $encrypted = trim((string) ($row['basic_password_enc'] ?? ''));
        if ($encrypted === '') {
            return null;
        }

        try {
            $decrypted = $this->settingsCryptoGateway->decryptString($encrypted);
        } catch (\Throwable) {
            return null;
        }

        $decrypted = trim($decrypted);

        return $decrypted !== '' ? $decrypted : null;
    }

    public function getOAuthTenantId(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['oauth_tenant_id'] ?? ''));

        return $value !== '' ? $value : null;
    }

    public function getOAuthClientId(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['oauth_client_id'] ?? ''));

        return $value !== '' ? $value : null;
    }

    public function getOAuthClientSecret(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $encrypted = trim((string) ($row['oauth_client_secret_enc'] ?? ''));
        if ($encrypted === '') {
            return null;
        }

        try {
            $decrypted = $this->settingsCryptoGateway->decryptString($encrypted);
        } catch (\Throwable) {
            return null;
        }

        $decrypted = trim($decrypted);

        return $decrypted !== '' ? $decrypted : null;
    }

    public function getOAuthTokenEndpoint(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['oauth_token_endpoint'] ?? ''));

        return $value !== '' ? $value : null;
    }

    public function getSystemRecommendationsConfig(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['system_recommendations_config'] ?? ''));

        return $value !== '' ? $value : null;
    }

    public function getControllingRiskConfig(int $tenantId): ?string
    {
        $row = $this->resolveRow($tenantId);
        $value = trim((string) ($row['controlling_risk_config'] ?? ''));

        return $value !== '' ? $value : null;
    }

    /**
     * Persist tenant override settings. Secrets are encrypted before storage.
     *
     * @param array<string, mixed> $data Field values (plain-text passwords/secrets accepted)
     */
    public function save(int $tenantId, array $data): bool
    {
        if ($tenantId <= 0) {
            return false;
        }

        // Encrypt secrets before storage
        if (isset($data['basic_password']) && is_string($data['basic_password'])) {
            $password = trim($data['basic_password']);
            unset($data['basic_password']);
            if ($password !== '') {
                try {
                    $data['basic_password_enc'] = $this->settingsCryptoGateway->encryptString($password);
                } catch (\Throwable) {
                    return false;
                }
            }
        }

        if (isset($data['oauth_client_secret']) && is_string($data['oauth_client_secret'])) {
            $secret = trim($data['oauth_client_secret']);
            unset($data['oauth_client_secret']);
            if ($secret !== '') {
                try {
                    $data['oauth_client_secret_enc'] = $this->settingsCryptoGateway->encryptString($secret);
                } catch (\Throwable) {
                    return false;
                }
            }
        }

        // Invalidate cache after write
        unset($this->rowCache[$tenantId]);

        return $this->repository->upsert($tenantId, $data);
    }

    public function deleteByTenantId(int $tenantId): bool
    {
        unset($this->rowCache[$tenantId]);

        return $this->repository->deleteByTenantId($tenantId);
    }

    /**
     * Load row once per tenant per request. Returns null if no row exists.
     *
     * @return array<string, mixed>|null
     */
    private function resolveRow(int $tenantId): ?array
    {
        if ($tenantId <= 0) {
            return null;
        }

        if (array_key_exists($tenantId, $this->rowCache)) {
            $cached = $this->rowCache[$tenantId];

            return $cached === false ? null : $cached;
        }

        $row = $this->repository->findByTenantId($tenantId);
        $this->rowCache[$tenantId] = $row ?? false;

        return $row;
    }
}
