<?php

namespace App\Services\Audit;

use App\Models\AuditLog;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class AuditLogService
{
    public function record(Request $request, $response = null, string $status = 'success'): void
    {
        try {
            $user = $request->user();
            if (!$user) {
                return;
            }

            AuditLog::create([
                'user_id' => $user->id,
                'user_email' => $user->email,
                'user_role' => $user->userrole,
                'module' => $this->moduleFromPath($request->path()),
                'action' => $this->actionFromRequest($request),
                'method' => $request->method(),
                'url' => $this->limit($request->fullUrl(), 1000),
                'ip_address' => $this->limit($request->ip(), 100),
                'user_agent' => $this->limit((string) $request->userAgent(), 500),
                'status' => $status,
                'response_code' => method_exists($response, 'getStatusCode') ? $response->getStatusCode() : null,
                'request_payload' => $this->payloadForStorage($request),
            ]);
        } catch (\Throwable $e) {
            // Do not break user workflow because audit logging failed.
        }
    }


    /**
     * Record a business-level audit event where a controller knows exactly what changed.
     * This complements the HTTP middleware audit and makes creates/updates visible in plain language.
     */
    public function recordBusinessEvent(string $module, string $action, array $context = [], string $status = 'success'): void
    {
        try {
            $request = request();
            $user = $request->user();

            AuditLog::create([
                'user_id' => $user ? $user->id : null,
                'user_email' => $user ? $user->email : null,
                'user_role' => $user ? $user->userrole : null,
                'module' => $this->limit($module, 100),
                'action' => $this->limit($action, 150),
                'method' => $request->method(),
                'url' => $this->limit($request->fullUrl(), 1000),
                'ip_address' => $this->limit($request->ip(), 100),
                'user_agent' => $this->limit((string) $request->userAgent(), 500),
                'status' => $status,
                'response_code' => 200,
                'request_payload' => $this->jsonPreview($this->sanitize($context)),
            ]);
        } catch (\Throwable $e) {
            // Audit failure must never break the main business workflow.
        }
    }

    private function moduleFromPath(string $path): string
    {
        $first = explode('/', trim($path, '/'))[0] ?? 'system';
        return $first ?: 'system';
    }

    private function sanitize(array $payload): array
    {
        $clean = [];
        foreach ($payload as $key => $value) {
            $clean[$key] = $this->sanitizeValue((string) $key, $value);
        }
        return $clean;
    }

    private function sanitizeValue(string $key, $value)
    {
        $blocked = ['password','password_confirmation','password_confirmatio','current_password','token','api_key','client_secret','client_secret_hash','secret','authorization','cookie','x-client-secret','x-signature'];

        $lower = strtolower($key);
        if (in_array($lower, $blocked, true) || str_contains($lower, 'password') || str_contains($lower, 'secret') || str_contains($lower, 'token')) {
            return '***MASKED***';
        }

        if (is_array($value)) {
            $nested = [];
            foreach ($value as $nestedKey => $nestedValue) {
                $nested[$nestedKey] = $this->sanitizeValue((string) $nestedKey, $nestedValue);
            }
            return $nested;
        }

        if (is_string($value) && strlen($value) > 1000) {
            return substr($value, 0, 1000).'...';
        }

        return $value;
    }

    private function actionFromRequest(Request $request): string
    {
        $path = trim($request->path(), '/');
        $map = [
            'dataentry/processvictimdata' => 'create_survivor_profile',
            'dataentry/updatevictimdata' => 'update_survivor_profile',
            'dataentry/processincidentdata' => 'create_incident',
            'dataentry/updateincidentdata' => 'update_incident',
            'dataentry/processperpetrator' => 'create_perpetrator',
            'dataentry/updateperpetrator' => 'update_perpetrator',
            'dataentry/processwitness' => 'create_witness',
            'dataentry/updatewitness' => 'update_witness',
            'dataentry/processaction' => 'create_survivor_service_action',
            'dataentry/updateaction' => 'update_survivor_service_action',
            'dataentry/processstatus' => 'create_case_status',
            'dataentry/updatestatus' => 'update_case_status',
        ];

        return $map[$path] ?? strtolower($request->method());
    }

    private function payloadForStorage(Request $request): string
    {
        return $this->jsonPreview($this->sanitize($request->except(['_token'])));
    }

    private function jsonPreview(array $payload): string
    {
        $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $json = $json === false ? '{}' : $json;

        return $this->limit($json, $this->auditPayloadLimit());
    }

    private function auditPayloadLimit(): int
    {
        // Keep below common TEXT limit. If the deployment uses LONGTEXT this is still safe.
        return 60000;
    }

    private function limit($value, int $limit): ?string
    {
        if ($value === null) {
            return null;
        }

        return Str::limit((string) $value, $limit, '');
    }

}
