<?php

declare(strict_types=1);

namespace App\Http\Controllers\Admin;

use App\Domain\Webhook\Services\WebhookPlayground;
use App\Http\Controllers\Controller;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
use Inertia\Response;

/**
 * Admin: Webhook Playground.
 *
 * Sandbox for testing both inbound (panel receives) and outbound (panel
 * sends) webhooks. Backed by {@see WebhookPlayground} service for I/O,
 * append-only log in `webhook_playground_requests`.
 */
class WebhookPlaygroundController extends Controller
{
    public function __construct(private readonly WebhookPlayground $service) {}

    public function index(): Response
    {
        $inboxes = DB::table('webhook_playground_inboxes')
            ->orderByDesc('id')
            ->get()
            ->map(function (object $inbox): array {
                $count = DB::table('webhook_playground_requests')
                    ->where('slug', $inbox->slug)
                    ->where('direction', 'inbound')
                    ->count();
                $lastAt = DB::table('webhook_playground_requests')
                    ->where('slug', $inbox->slug)
                    ->where('direction', 'inbound')
                    ->max('created_at');

                return [
                    'id' => $inbox->id,
                    'slug' => $inbox->slug,
                    'label' => $inbox->label,
                    'has_secret' => ! empty($inbox->hmac_secret),
                    'received_count' => $count,
                    'last_received_at' => $lastAt,
                    'url' => url("/webhooks/playground/{$inbox->slug}"),
                    'created_at' => $inbox->created_at,
                ];
            });

        $inbound = DB::table('webhook_playground_requests')
            ->where('direction', 'inbound')
            ->orderByDesc('id')
            ->limit(50)
            ->get(['id', 'slug', 'method', 'response_status', 'hmac_valid', 'duration_ms', 'client_ip', 'created_at']);

        $outbound = DB::table('webhook_playground_requests')
            ->where('direction', 'outbound')
            ->orderByDesc('id')
            ->limit(50)
            ->get(['id', 'slug', 'method', 'url', 'response_status', 'duration_ms', 'created_at']);

        return Inertia::render('Admin/WebhookPlayground/Index', [
            'inboxes' => $inboxes,
            'inbound' => $inbound,
            'outbound' => $outbound,
        ]);
    }

    public function createInbox(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'label' => ['nullable', 'string', 'max:100'],
            'hmac_secret' => ['nullable', 'string', 'max:200'],
        ]);

        $slug = $this->service->createInbox(
            $validated['label'] ?? null,
            $validated['hmac_secret'] ?? null,
        );

        return back()->with('success', 'Inbox oluşturuldu: '.$slug);
    }

    public function deleteInbox(Request $request, string $slug): RedirectResponse
    {
        DB::transaction(function () use ($slug): void {
            DB::table('webhook_playground_requests')->where('slug', $slug)->delete();
            DB::table('webhook_playground_inboxes')->where('slug', $slug)->delete();
        });

        return back()->with('success', 'Inbox silindi.');
    }

    public function viewRequest(Request $request, int $id): Response
    {
        $row = DB::table('webhook_playground_requests')->find($id);
        abort_unless($row, 404);

        return Inertia::render('Admin/WebhookPlayground/Show', [
            'request' => [
                'id' => $row->id,
                'slug' => $row->slug,
                'direction' => $row->direction,
                'method' => $row->method,
                'url' => $row->url,
                'headers' => json_decode((string) $row->headers, true) ?: new \stdClass,
                'body' => $row->body,
                'body_truncated' => (bool) $row->body_truncated,
                'response_status' => $row->response_status,
                'response_headers' => $row->response_headers
                    ? (json_decode((string) $row->response_headers, true) ?: new \stdClass)
                    : null,
                'response_body' => $row->response_body,
                'hmac_valid' => $row->hmac_valid !== null ? (bool) $row->hmac_valid : null,
                'duration_ms' => $row->duration_ms,
                'client_ip' => $row->client_ip,
                'user_agent' => $row->user_agent,
                'created_at' => $row->created_at,
            ],
        ]);
    }

    public function sendOutbound(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'url' => ['required', 'url', 'max:1024'],
            'method' => ['required', 'in:GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS'],
            'headers' => ['nullable', 'array'],
            'headers.*' => ['nullable', 'string', 'max:1000'],
            'body' => ['nullable', 'string', 'max:65536'],
            'hmac_secret' => ['nullable', 'string', 'max:200'],
        ]);

        $result = $this->service->sendOutbound([
            'url' => $validated['url'],
            'method' => $validated['method'],
            'headers' => $validated['headers'] ?? [],
            'body' => $validated['body'] ?? '',
            'hmac_secret' => $validated['hmac_secret'] ?? null,
        ]);

        return response()->json($result);
    }

    public function replay(Request $request, int $id): JsonResponse
    {
        $row = DB::table('webhook_playground_requests')->find($id);
        abort_unless($row, 404);

        $result = $this->service->replay($id);

        return response()->json($result);
    }

    /**
     * Public receiver. Mounted at /webhooks/playground/{slug}; accepts any
     * HTTP verb, persists the request, returns JSON. CSRF-exempt + throttled
     * via middleware in routes/web.php. Heavy lifting stays under 500ms:
     * a single INSERT is the only DB op on the hot path.
     */
    public function inboundReceive(Request $request, string $slug): JsonResponse
    {
        $start = microtime(true);

        $inbox = DB::table('webhook_playground_inboxes')->where('slug', $slug)->first();
        if (! $inbox) {
            return response()->json(['ok' => false, 'error' => 'Inbox not found'], 404);
        }

        $hmacValid = $this->service->verifyHmac($slug, $request);

        $duration = (int) ((microtime(true) - $start) * 1000);

        $rowId = $this->service->recordInbound($slug, $request, $hmacValid, $duration);

        return response()->json([
            'ok' => true,
            'recorded_id' => $rowId,
        ]);
    }
}
