<?php

namespace App\Http\Controllers;

use App\Jobs\GenerateDocumentationJob;
use App\Services\NotionService;
use App\Support\NotionTask;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Log;

class NotionWebhookController extends Controller
{
    /**
     * Notion property ID of "Dev-Status" in the Tasks database.
     * Used to ignore events that touched unrelated properties.
     */
    private const DEV_STATUS_PROPERTY_ID = 'bnO;';

    public function __construct(private readonly NotionService $notion) {}

    /**
     * Handle incoming Notion webhook.
     *
     * Notion sends:
     *  - a one-time verification payload when the subscription is created;
     *  - `page.properties_updated` events when task properties change.
     */
    public function handle(Request $request): Response
    {
        $payload = $request->json()->all();

        // 1. Subscription verification handshake — Notion sends the token once.
        //    Log it so it can be pasted into the integration's webhook settings.
        if (isset($payload['verification_token'])) {
            Log::info('Notion webhook: verification token received', [
                'verification_token' => $payload['verification_token'],
            ]);
            return response('OK', 200);
        }

        // 2. Verify HMAC signature
        if (! $this->verifySignature($request)) {
            Log::warning('Notion webhook: invalid signature', ['ip' => $request->ip()]);
            return response('Unauthorized', 401);
        }

        $type   = $payload['type'] ?? 'unknown';
        $pageId = $payload['entity']['id'] ?? null;

        Log::info('Notion webhook received', ['event' => $type, 'page_id' => $pageId]);

        // 3. Only act on property updates of pages
        if ($type !== 'page.properties_updated' || ! $pageId) {
            return response('OK', 200);
        }

        // 4. Skip events that didn't touch Dev-Status (when Notion tells us)
        $updated = $payload['data']['updated_properties'] ?? null;
        if (is_array($updated) && ! in_array(self::DEV_STATUS_PROPERTY_ID, $updated, true)) {
            return response('OK', 200);
        }

        // 5. Fetch the page and check the current Dev-Status value.
        //    (The webhook payload carries property IDs only, not values.)
        try {
            $page = $this->notion->getPage($pageId);
        } catch (\Throwable $e) {
            Log::error('Notion webhook: failed to fetch page', [
                'page_id' => $pageId,
                'error'   => $e->getMessage(),
            ]);
            return response('OK', 200);
        }

        $triggerStatus = config('services.notion.trigger_status', 'Done');

        if (NotionTask::devStatus($page) !== $triggerStatus) {
            return response('OK', 200);
        }

        if (NotionTask::documentationStatus($page) === 'Generated') {
            return response('OK', 200); // already documented — idempotent
        }

        // 6. Dispatch async job (non-blocking — Claude can take 5-15s)
        GenerateDocumentationJob::dispatch($pageId);

        Log::info('GenerateDocumentationJob dispatched', ['page_id' => $pageId]);

        return response('Accepted', 202);
    }

    /**
     * Verify Notion HMAC-SHA256 webhook signature.
     * Notion signs with the verification token using the raw request body.
     */
    private function verifySignature(Request $request): bool
    {
        $secret    = config('services.notion.webhook_secret');
        $signature = $request->header('X-Notion-Signature');

        if (! $signature || ! $secret) {
            return false;
        }

        $expected = 'sha256=' . hash_hmac(
            'sha256',
            $request->getContent(),
            $secret
        );

        return hash_equals($expected, $signature);
    }
}
