<?php

namespace App\Http\Controllers;

use App\Models\Contact;
use App\Models\ContactList;
use App\Models\Form;
use App\Models\Notification;
use App\Models\Tag;
use Illuminate\Http\Request;

/**
 * Public inbound endpoint for the embedded/popup form snippets generated by
 * FormEmbedBuilder. No session/CSRF (registered under routes/api.php, same
 * as WebhookReceiveController), identified only by the form's unguessable
 * UUID. Field-to-Contact mapping uses each field's own stored
 * "contact_field" (set in the builder), not the generic alias guessing
 * WebhookFieldMapper does for arbitrary third-party payloads.
 */
class FormSubmitController extends Controller
{
    private const CONTACT_COLUMNS = [
        'first_name', 'last_name', 'phone', 'date_of_birth',
        'address_line_1', 'address_line_2', 'city', 'state', 'postal_code', 'country',
    ];

    public function store(Request $request, string $uuid)
    {
        $form = Form::where('uuid', $uuid)->first();

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

        if ($form->status !== 'active') {
            return response()->json(['success' => false, 'message' => 'This form is not published yet. Set it to Active in the builder to start accepting submissions.'], 404);
        }

        [$email, $attributes] = $this->mapSubmission($form->fields ?? [], $request->all());

        if (empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) {
            return $this->respond($form, false, 'A valid email address is required.', 422);
        }

        $email = strtolower($email);
        // Scoped explicitly to the form'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', $form->user_id)->where('email', $email)->first();

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

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

        $formListIds = $form->lists->pluck('id')->toArray();
        $formTagIds  = $form->tags->pluck('id')->toArray();

        $contact->lists()->syncWithoutDetaching($formListIds);
        $contact->tags()->syncWithoutDetaching($formTagIds);

        foreach (array_diff($formListIds, $existingListIds) as $listId) {
            $listTitle = ContactList::find($listId)?->title ?? "#{$listId}";
            $contact->logActivity('list_added', "Added to list: {$listTitle}");
        }

        foreach (array_diff($formTagIds, $existingTagIds) as $tagId) {
            $tagTitle = Tag::find($tagId)?->title ?? "#{$tagId}";
            $contact->logActivity('tag_added', "Tag added: {$tagTitle}");
        }

        $form->increment('total_submissions');
        $form->submissions()->create([
            'contact_id' => $contact->id,
            'email'      => $email,
        ]);

        Notification::notify(
            $form->user_id,
            'success',
            'New Form Submission',
            'Form "' . $form->name . '" received a new submission: ' . $email . '.',
            '/forms'
        );

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

    /**
     * @return array{0: ?string, 1: array<string, string>}
     */
    private function mapSubmission(array $blocks, array $payload): array
    {
        $email      = null;
        $attributes = [];

        foreach ($this->collectFieldBlocks($blocks) as $field) {
            $id           = $field['id'] ?? null;
            $type         = $field['type'] ?? null;
            $contactField = $field['contact_field'] ?? null;

            if (!$id || $type === 'field-hidden') {
                continue;
            }

            if ($type === 'field-name') {
                $first  = trim((string) ($payload[$id . '_first'] ?? ''));
                $second = trim((string) ($payload[$id . '_second'] ?? ''));
                if ($first !== '') {
                    $attributes['first_name'] = $first;
                }
                if ($second !== '') {
                    $attributes['last_name'] = $second;
                }
                continue;
            }

            $value = $payload[$id] ?? null;
            $value = is_array($value) ? implode(', ', $value) : trim((string) $value);

            if ($value === '') {
                continue;
            }

            if ($type === 'field-email' && !$contactField) {
                $contactField = 'email';
            }

            if ($contactField === 'email') {
                $email = $value;
                continue;
            }

            if ($contactField && in_array($contactField, self::CONTACT_COLUMNS, true)) {
                $attributes[$contactField] = $value;
            }
        }

        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']);
            }
        }

        return [$email, $attributes];
    }

    /**
     * Flattens every field-* block out of the form's block tree, descending
     * into column blocks' nested columns[].blocks[]. Content blocks
     * (heading/text/image/button/etc.) are skipped since they don't collect
     * data.
     *
     * @return array<int, array>
     */
    private function collectFieldBlocks(array $blocks): array
    {
        $fields = [];

        foreach ($blocks as $block) {
            $type = $block['type'] ?? '';

            if (str_starts_with($type, 'field-')) {
                $fields[] = $block;
                continue;
            }

            if ($type === 'column') {
                foreach ($block['columns'] ?? [] as $column) {
                    $fields = array_merge($fields, $this->collectFieldBlocks($column['blocks'] ?? []));
                }
            }
        }

        return $fields;
    }

    private function respond(Form $form, bool $success, string $message, int $status, ?Contact $contact = null)
    {
        $settings = $form->settings ?? [];

        // Plain HTML <form> submissions (JS disabled) have nowhere to show
        // JSON - let them redirect instead, same convention as
        // WebhookReceiveController::respond().
        if ($success && ($settings['after_submit_action'] ?? null) === 'redirect' && !empty($settings['redirect_url'])) {
            return redirect()->away($settings['redirect_url']);
        }

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