<?php

namespace App\Services\Api;

use App\Models\ApiClient;
use App\Models\IntegrationLog;
use App\Services\Integrations\IntegrationLogService;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class ApiAccessAuditService
{
    private $logs;

    public function __construct(IntegrationLogService $logs)
    {
        $this->logs = $logs;
    }

    public function start(Request $request, ?ApiClient $client, ?string $requiredScope = null, string $signatureStatus = null, int $rateLimit = null, int $rateRemaining = null): ?IntegrationLog
    {
        if (! Schema::hasTable('integration_logs')) {
            return null;
        }

        $requestId = (string) Str::uuid();
        $request->attributes->set('ngbvd_api_request_id', $requestId);

        $body = (string) $request->getContent();
        $query = $request->query->all();

        return $this->logs->start([
            'api_client_id' => $client ? $client->id : null,
            'integration_name' => $client ? ($client->integration_code ?: 'API_CLIENT') : 'API_ACCESS',
            'system_name' => $client ? ($client->system_name ?: $client->name) : 'Unknown API Client',
            'direction' => 'inbound',
            'endpoint' => '/' . ltrim($request->path(), '/'),
            'http_method' => $request->method(),
            'record_type' => $this->recordTypeFor($request, $requiredScope),
            'request_payload_hash' => $this->logs->hashPayload([
                'method' => $request->method(),
                'path' => $request->path(),
                'query' => $query,
                'body_sha256' => hash('sha256', $body),
            ]),
            'request_payload_preview' => $this->requestPayloadPreview($request),
            'status' => 'pending',
            'request_id' => $requestId,
            'route_name' => optional($request->route())->getName(),
            'request_url' => $request->fullUrl(),
            'request_query_preview' => $query,
            'request_headers_preview' => $this->safeHeaders($request),
            'ip_address' => $request->ip(),
            'forwarded_for' => $request->header('X-Forwarded-For'),
            'user_agent' => (string) $request->userAgent(),
            'data_accessed' => $this->dataAccessedFor($request, $requiredScope),
            'data_scope' => $client ? $this->scopeSummary($client, $requiredScope) : null,
            'allowed_district_ids' => $client ? (string) ($client->allowed_district_ids ?? '') : null,
            'partner_id' => $client ? ($client->partner_id ?? null) : null,
            'is_signed_request' => $request->headers->has('X-Signature'),
            'signature_status' => $signatureStatus,
            'is_encrypted_payload' => (bool) $request->input('encrypted', false),
            'rate_limit_per_minute' => $rateLimit,
            'rate_limit_remaining' => $rateRemaining,
            'request_started_at' => now(),
        ]);
    }

    public function denied(Request $request, ?ApiClient $client, string $message, int $statusCode, ?string $requiredScope = null, string $signatureStatus = null): void
    {
        $log = $this->start($request, $client, $requiredScope, $signatureStatus);

        if (! $log) {
            return;
        }

        $log->fill($this->logs->sanitize([
            'status' => 'failed',
            'response_code' => $statusCode,
            'error_message' => $message,
            'response_body_preview' => [
                'success' => false,
                'message' => $message,
            ],
            'request_completed_at' => now(),
            'duration_ms' => 0,
        ]))->save();
    }

    public function complete(?IntegrationLog $log, Request $request, SymfonyResponse $response, float $startedAt): void
    {
        if (! $log) {
            return;
        }

        $durationMs = (int) round((microtime(true) - $startedAt) * 1000);
        $content = method_exists($response, 'getContent') ? (string) $response->getContent() : '';
        $summary = $this->responseSummary($content);

        $statusCode = (int) $response->getStatusCode();
        $success = $statusCode >= 200 && $statusCode < 400;

        $log->fill($this->logs->sanitize([
            'status' => $success ? 'success' : 'failed',
            'response_code' => $statusCode,
            'response_body_preview' => $summary,
            'error_message' => $success ? null : 'API request completed with HTTP '.$statusCode,
            'duration_ms' => $durationMs,
            'response_size_bytes' => strlen($content),
            'response_record_count' => $summary['record_count'] ?? null,
            'data_accessed' => $summary['data_accessed'] ?? $log->data_accessed,
            'request_completed_at' => now(),
            'last_attempt_at' => now(),
        ]))->save();
    }

    private function requestPayloadPreview(Request $request): array
    {
        $body = (string) $request->getContent();

        return [
            'method' => $request->method(),
            'path' => '/' . ltrim($request->path(), '/'),
            'query' => $request->query->all(),
            'body_sha256' => hash('sha256', $body),
            'body_preview' => $request->isMethod('GET') ? null : mb_substr($body, 0, 1500),
        ];
    }

    private function safeHeaders(Request $request): array
    {
        return [
            'accept' => $request->header('Accept'),
            'content_type' => $request->header('Content-Type'),
            'x_client_key' => $this->previewKey($request->header('X-Client-Key')),
            'x_timestamp' => $request->header('X-Timestamp'),
            'x_nonce' => $request->header('X-Nonce'),
            'x_signature_present' => $request->headers->has('X-Signature'),
            'x_content_sha256' => $request->header('X-Content-SHA256'),
            'x_forwarded_for' => $request->header('X-Forwarded-For'),
            'x_forwarded_proto' => $request->header('X-Forwarded-Proto'),
            'user_agent' => $request->userAgent(),
        ];
    }

    private function previewKey(?string $key): ?string
    {
        if (! $key) {
            return null;
        }

        return substr($key, 0, 10).'...'.substr($key, -6);
    }

    private function recordTypeFor(Request $request, ?string $requiredScope): string
    {
        if ($requiredScope === 'reference_data.read') {
            return 'reference_data';
        }

        if ($requiredScope === 'incident_statistics.read') {
            return 'incident_statistics';
        }

        if (in_array($requiredScope, ['ubos.push', 'nsr.push'], true)) {
            return 'case_push';
        }

        if ($request->isMethod('GET')) {
            return 'api_get';
        }

        return 'api_post';
    }

    private function dataAccessedFor(Request $request, ?string $requiredScope): array
    {
        return [
            'scope' => $requiredScope,
            'route' => optional($request->route())->getName(),
            'path' => '/' . ltrim($request->path(), '/'),
            'method' => $request->method(),
            'query_keys' => array_keys($request->query->all()),
        ];
    }

    private function scopeSummary(ApiClient $client, ?string $requiredScope): string
    {
        $scopes = method_exists($client, 'scopeList') ? $client->scopeList() : preg_split('/[,\s]+/', (string) $client->allowed_scopes, -1, PREG_SPLIT_NO_EMPTY);

        $dataScopes = array_values(array_filter($scopes ?: [], function ($scope) {
            return strpos($scope, 'data_scope.') === 0;
        }));

        return json_encode([
            'required_scope' => $requiredScope,
            'data_scopes' => $dataScopes,
            'allowed_district_ids' => $client->allowed_district_ids ?? null,
            'partner_id' => $client->partner_id ?? null,
        ]);
    }

    private function responseSummary(string $content): array
    {
        $summary = [
            'record_count' => null,
            'data_accessed' => null,
        ];

        $json = json_decode($content, true);
        if (! is_array($json)) {
            $summary['preview'] = mb_substr($content, 0, 1200);
            return $summary;
        }

        $summary['success'] = $json['success'] ?? null;

        if (array_key_exists('metadata', $json)) {
            $summary['metadata'] = $json['metadata'];
        }

        if (array_key_exists('log_uuid', $json)) {
            $summary['log_uuid'] = $json['log_uuid'];
        }

        if (array_key_exists('data', $json)) {
            $data = $json['data'];
            if (is_array($data)) {
                $counts = [];
                $total = 0;

                foreach ($data as $key => $value) {
                    if (is_array($value)) {
                        $count = count($value);
                        $counts[$key] = $count;
                        $total += $count;
                    }
                }

                if (! empty($counts)) {
                    $summary['data_counts'] = $counts;
                    $summary['record_count'] = $total;
                    $summary['data_accessed'] = [
                        'type' => 'json_data_object',
                        'keys' => array_keys($data),
                        'counts' => $counts,
                    ];
                } else {
                    $summary['record_count'] = count($data);
                    $summary['data_accessed'] = [
                        'type' => 'json_data_array',
                        'count' => count($data),
                    ];
                }
            }
        }

        return $summary;
    }
}
