<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Str;

class ApiClient extends Model
{
    protected $fillable = [
        'uuid',
        'name',
        'system_name',
        'integration_code',
        'client_key',
        'client_secret_hash',
        'client_secret_encrypted',
        'signing_secret_encrypted',
        'encryption_key_encrypted',
        'allowed_ips',
        'allowed_scopes',
        'allowed_district_ids',
        'partner_id',
        'is_active',
        'outbound_enabled',
        'outbound_base_url',
        'outbound_push_endpoint',
        'outbound_timeout',
        'outbound_verify_ssl',
        'outbound_api_version',
        'outbound_notes',
        'require_signed_requests',
        'require_payload_encryption',
        'require_https',
        'rate_limit_per_minute',
        'signature_window_seconds',
        'failed_attempts',
        'last_failed_at',
        'last_used_at',
        'created_by',
        'updated_by',
    ];

    protected $casts = [
        'is_active' => 'boolean',
        'outbound_enabled' => 'boolean',
        'outbound_verify_ssl' => 'boolean',
        'outbound_timeout' => 'integer',
        'require_signed_requests' => 'boolean',
        'require_payload_encryption' => 'boolean',
        'require_https' => 'boolean',
        'rate_limit_per_minute' => 'integer',
        'signature_window_seconds' => 'integer',
        'failed_attempts' => 'integer',
        'last_failed_at' => 'datetime',
        'last_used_at' => 'datetime',
    ];

    protected static function booted()
    {
        static::creating(function ($model) {
            if (empty($model->uuid)) {
                $model->uuid = (string) Str::uuid();
            }

            if (empty($model->integration_code)) {
                $model->integration_code = static::guessIntegrationCode(
                    (string) $model->system_name,
                    (string) $model->allowed_scopes
                );
            }
        });
    }

    public static function guessIntegrationCode(string $systemName = '', string $scopes = ''): string
    {
        $haystack = strtolower($systemName.' '.$scopes);

        if (strpos($haystack, 'ubos') !== false || strpos($haystack, 'ubos.push') !== false) {
            return 'UBOS_GBV_PORTAL';
        }

        if (strpos($haystack, 'nsr') !== false || strpos($haystack, 'registry') !== false || strpos($haystack, 'nsr.push') !== false) {
            return 'NSR';
        }

        if (strpos($haystack, 'sauti') !== false) {
            return 'SAUTI';
        }

        return 'PARTNER_SYSTEM';
    }

    public function ipList(): array
    {
        return $this->normalisedList($this->allowed_ips);
    }

    public function scopeList(): array
    {
        return $this->normalisedList($this->allowed_scopes);
    }

    public function districtIdList(): array
    {
        return array_values(array_unique(array_filter(array_map(function ($value) {
            return (string) $value;
        }, $this->normalisedList($this->allowed_district_ids)), function ($value) {
            return $value !== '' && $value !== '*';
        })));
    }

    public function canUseScope(string $scope): bool
    {
        $scopes = $this->scopeList();

        return in_array('*', $scopes, true) || in_array($scope, $scopes, true);
    }

    public function hasDataScope(string $scope): bool
    {
        return $this->canUseScope('data_scope.' . $scope);
    }

    public function hasAnyDataScope(): bool
    {
        foreach (['national', 'district', 'partner', 'own_records'] as $scope) {
            if ($this->hasDataScope($scope)) {
                return true;
            }
        }

        return false;
    }

    public function dataScopeList(): array
    {
        return array_values(array_filter($this->scopeList(), function ($scope) {
            return strpos($scope, 'data_scope.') === 0;
        }));
    }

    public function hasOutboundConfiguration(): bool
    {
        return (bool) $this->outbound_enabled && ! empty($this->outbound_base_url);
    }

    public function outboundEndpointUrl(?string $defaultEndpoint = null): ?string
    {
        if (empty($this->outbound_base_url)) {
            return null;
        }

        $endpoint = $this->outbound_push_endpoint ?: $defaultEndpoint ?: '';

        return rtrim((string) $this->outbound_base_url, '/').'/'.ltrim((string) $endpoint, '/');
    }

    public function signingSecret(): ?string
    {
        foreach (['signing_secret_encrypted', 'client_secret_encrypted'] as $field) {
            $value = $this->{$field} ?? null;
            if (! empty($value)) {
                try {
                    return Crypt::decryptString($value);
                } catch (\Throwable $e) {
                    // Try the next available field.
                }
            }
        }

        return null;
    }

    public function encryptionKey(): ?string
    {
        if (empty($this->encryption_key_encrypted)) {
            return null;
        }

        try {
            return Crypt::decryptString($this->encryption_key_encrypted);
        } catch (\Throwable $e) {
            return null;
        }
    }

    private function normalisedList($value): array
    {
        if ($value === null || $value === '') {
            return [];
        }

        if (is_array($value)) {
            return array_values(array_filter(array_map(function ($item) {
                return trim((string) $item);
            }, $value), function ($item) {
                return $item !== '';
            }));
        }

        $text = trim((string) $value);

        if ($text === '' || $text === '*') {
            return $text === '*' ? ['*'] : [];
        }

        // Supports stored JSON arrays as well as comma/space-separated text.
        if ((substr($text, 0, 1) === '[' && substr($text, -1) === ']') || (substr($text, 0, 1) === '{' && substr($text, -1) === '}')) {
            $decoded = json_decode($text, true);
            if (is_array($decoded)) {
                return $this->normalisedList($decoded);
            }
        }

        return array_values(array_filter(array_map(function ($item) {
            return trim((string) $item);
        }, preg_split('/[,\s]+/', $text)), function ($item) {
            return $item !== '';
        }));
    }
}
