<?php

namespace App\Http\Controllers;

use App\Models\Contact;
use App\Models\Webhook;
use App\Models\WebhookLog;
use App\Services\ContactFieldRegistry;
use App\Services\WebhookFieldMapper;
use Illuminate\Http\Request;

/**
 * Public inbound endpoint - external forms/tools POST here to create or
 * update a Contact. No session/CSRF (registered under routes/api.php),
 * identified only by the webhook's unguessable UUID plus an optional HMAC
 * signature. See routes/api.php for the throttle applied to this route.
 */
class WebhookReceiveController extends Controller
{
    public function store(Request $request, string $uuid)
    {
        $webhook = Webhook::where('uuid', $uuid)->first();

        if (!$webhook || !$webhook->is_active) {
            return response()->json(['success' => false, 'message' => 'Webhook not found.'], 404);
        }

        if ($secret = $webhook->getRawOriginal('secret')) {
            $signature = (string) $request->header('X-Webhook-Signature', '');
            $expected  = hash_hmac('sha256', $request->getContent(), $secret);

            if ($signature === '' || !hash_equals($expected, $signature)) {
                $this->log($webhook, 'rejected', 'Invalid or missing signature.', $request);

                \App\Models\Notification::notify(
                    $webhook->user_id,
                    'error',
                    'Webhook Signature Rejected',
                    'Incoming request for webhook "' . $webhook->name . '" was rejected due to invalid or missing signature.',
                    '/webhooks'
                );

                return response()->json(['success' => false, 'message' => 'Invalid signature.'], 401);
            }
        }

        $payload = $request->all();
        $fields  = WebhookFieldMapper::map($payload);

        if (empty($fields['email']) || !filter_var($fields['email'], FILTER_VALIDATE_EMAIL)) {
            $this->log($webhook, 'rejected', 'No valid email field found in payload.', $request);

            \App\Models\Notification::notify(
                $webhook->user_id,
                'error',
                'Webhook Rejected (Invalid Email)',
                'Incoming request for webhook "' . $webhook->name . '" failed: No valid email address found in payload.',
                '/webhooks'
            );

            return $this->respond($webhook, false, 'A valid email address is required.', 422);
        }

        $email    = strtolower($fields['email']);
        // Scoped explicitly to the webhook's owner  this is a public,
        // unauthenticated endpoint so the tenant global scope has no
        // session to key off of.
        $existing = Contact::where('user_id', $webhook->user_id)->where('email', $email)->first();

        if ($existing && $webhook->duplicate_handling === 'skip') {
            $contact = $existing;
            $message = 'Duplicate contact skipped.';
        } else {
            // Every mappable field WebhookFieldMapper can auto-detect, except
            // email (handled separately above as the lookup key).
            $mappableKeys = array_diff(array_keys(ContactFieldRegistry::aliases()), ['email']);
            $attributes   = array_filter(
                array_intersect_key($fields, array_flip($mappableKeys)),
                fn ($v) => $v !== null && $v !== ''
            );

            // Guard against unparseable dates crashing the save (Contact casts this to a date).
            if (isset($attributes['date_of_birth'])) {
                $parsed = strtotime($attributes['date_of_birth']);

                if ($parsed) {
                    $attributes['date_of_birth'] = date('Y-m-d', $parsed);
                } else {
                    unset($attributes['date_of_birth']);
                }
            }

            if ($existing) {
                $existing->fill($attributes)->save();
                $contact = $existing;
                $message = 'Contact updated.';
                $contact->logActivity('updated', "Updated via webhook \"{$webhook->name}\"");
            } else {
                $contact = Contact::create(array_merge($attributes, [
                    'user_id'      => $webhook->user_id,
                    'email'        => $email,
                    'status'       => $webhook->default_status,
                    'contact_type' => 'lead',
                    'source'       => "Webhook: {$webhook->name}",
                    'ip'           => $request->ip(),
                ]));
                $message = 'Contact created.';
                $contact->logActivity('created', "Created via webhook \"{$webhook->name}\"");
            }
        }

        $existingListIds = $contact->lists()->pluck('lists.id')->toArray();
        $existingTagIds  = $contact->tags()->pluck('tags.id')->toArray();

        $webhookListIds = $webhook->lists->pluck('id')->toArray();
        $webhookTagIds  = $webhook->tags->pluck('id')->toArray();

        $contact->lists()->syncWithoutDetaching($webhookListIds);
        $contact->tags()->syncWithoutDetaching($webhookTagIds);

        // Log newly added lists to trigger automations
        $newListIds = array_diff($webhookListIds, $existingListIds);
        foreach ($newListIds as $listId) {
            $listTitle = \App\Models\ContactList::find($listId)?->title ?? "#{$listId}";
            $contact->logActivity('list_added', "Added to list: {$listTitle}");
        }

        // Log newly added tags to trigger automations
        $newTagIds = array_diff($webhookTagIds, $existingTagIds);
        foreach ($newTagIds as $tagId) {
            $tagTitle = \App\Models\Tag::find($tagId)?->title ?? "#{$tagId}";
            $contact->logActivity('tag_added', "Tag added: {$tagTitle}");
        }

        $webhook->increment('total_received');
        $webhook->update(['last_triggered_at' => now()]);

        $this->log($webhook, 'success', $message, $request, $contact->id);

        // Log webhook received activity to evaluate trigger block automations
        $contact->logActivity('webhook_received', "Received webhook \"{$webhook->name}\" [id:{$webhook->id}]");

        // Enroll contact in active Automations with a canvas incoming_webhook node
        $activeAutomations = \App\Models\Campaign::where('type', 'automation')
            ->where('status', 'active')
            ->get();

        foreach ($activeAutomations as $automation) {
            $settings = $automation->settings ?? [];
            $nodes = $settings['nodes'] ?? [];
            foreach ($nodes as $node) {
                $nodeType = $node['type'] ?? $node['data']['type'] ?? '';
                if ($nodeType === 'incoming_webhook') {
                    $webhookId = $node['config']['webhook_id'] ?? $node['data']['webhook_id'] ?? null;
                    if (!$webhookId || $webhookId === 'all' || (int)$webhookId === (int)$webhook->id) {
                        \App\Services\AutomationEngine::enroll($automation, $contact, $node['id']);
                    }
                }
            }
        }

        \App\Models\Notification::notify(
            $webhook->user_id,
            'success',
            'Webhook Ingestion Successful',
            'Webhook "' . $webhook->name . '" successfully ingested contact: ' . $email . '.',
            '/webhooks'
        );

        return $this->respond($webhook, true, $message, 200, $contact);
    }

    private function respond(Webhook $webhook, bool $success, string $message, int $status, ?Contact $contact = null)
    {
        // Plain HTML <form> submissions have nowhere to display JSON - let
        // them redirect to a real page instead, same as Netlify Forms/Formspree.
        if ($success && $webhook->success_redirect_url) {
            return redirect()->away($webhook->success_redirect_url);
        }

        return response()->json(array_filter([
            'success'    => $success,
            'message'    => $message,
            'contact_id' => $contact?->id,
        ], fn ($v) => $v !== null), $status);
    }

    private function log(Webhook $webhook, string $status, string $message, Request $request, ?int $contactId = null): void
    {
        WebhookLog::create([
            'webhook_id' => $webhook->id,
            'status'     => $status,
            'message'    => $message,
            'payload'    => $request->all(),
            'ip_address' => $request->ip(),
            'contact_id' => $contactId,
        ]);
    }

    public function incomingWebhook(Request $request, int $campaignId, string $nodeId)
    {
        $automation = \App\Models\Campaign::where('id', $campaignId)
            ->where('type', 'automation')
            ->first();

        if (!$automation || $automation->status !== 'active') {
            return response()->json(['success' => false, 'message' => 'Automation not active or not found.'], 404);
        }

        $payload = $request->all();
        $fields  = WebhookFieldMapper::map($payload);

        if (empty($fields['email']) || !filter_var($fields['email'], FILTER_VALIDATE_EMAIL)) {
            return response()->json(['success' => false, 'message' => 'A valid email address is required.'], 422);
        }

        $email    = strtolower($fields['email']);
        $contact = Contact::where('user_id', $automation->user_id)->where('email', $email)->first();

        $mappableKeys = array_diff(array_keys(\App\Services\ContactFieldRegistry::aliases()), ['email']);
        $attributes   = array_filter(
            array_intersect_key($fields, array_flip($mappableKeys)),
            fn ($v) => $v !== null && $v !== ''
        );

        if (isset($attributes['date_of_birth'])) {
            $parsed = strtotime($attributes['date_of_birth']);
            if ($parsed) {
                $attributes['date_of_birth'] = date('Y-m-d', $parsed);
            } else {
                unset($attributes['date_of_birth']);
            }
        }

        if ($contact) {
            $contact->fill($attributes)->save();
            $contact->logActivity('updated', "Updated via Incoming Webhook trigger node in automation \"{$automation->title}\"");
        } else {
            $contact = Contact::create(array_merge($attributes, [
                'user_id' => $automation->user_id,
                'email'   => $email,
                'status'  => 'subscribed',
            ]));
            $contact->logActivity('created', "Created via Incoming Webhook trigger node in automation \"{$automation->title}\"");
        }

        \App\Services\AutomationEngine::enroll($automation, $contact, $nodeId);

        return response()->json([
            'success' => true,
            'message' => 'Contact processed and enrolled in automation successfully.',
            'contact' => [
                'id' => $contact->id,
                'email' => $contact->email
            ]
        ]);
    }
}
