<?php

namespace App\Actions\Contact;

use App\Actions\Support\ApiRateLimitedException;
use App\Actions\Support\ApiValidationException;
use App\Actions\Support\InteractsWithFloodLimiting;
use App\Enums\ContactBudget;
use App\Enums\ContactService;
use App\Models\ContactSubmission;
use Carbon\Exceptions\InvalidFormatException;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;

/**
 * Port of the legacy `api/submit-contact.php` endpoint.
 *
 * Preserves the legacy validation order, messages, and normalisation rules
 * exactly, since the existing front-end JS depends on them. See the ticket
 * for the full field-by-field contract; this class intentionally validates
 * imperatively (stopping at the first failure) rather than via a Form
 * Request, because the legacy endpoint returns only a single flat error
 * message rather than Laravel's default per-field `errors` shape.
 */
class SubmitContactForm
{
    use InteractsWithFloodLimiting;

    /**
     * @return array{submissionId: int|null}
     */
    public function handle(Request $request): array
    {
        $website = trim((string) $request->input('website', ''));

        // Honeypot: bots fill hidden fields humans never see. Report a fake
        // success and store nothing, without touching any rate limiter.
        if ($website !== '') {
            return ['submissionId' => null];
        }

        $ip = (string) $request->ip();

        // Flood limit: counts every attempt reaching this point, including
        // ones that go on to fail field validation below.
        if (! $this->attempt("contact-attempts:{$ip}", 20)) {
            throw new ApiRateLimitedException(
                'Too many attempts. Please call us on 9879991799 and we will help right away.'
            );
        }

        $attributes = $this->validate($request);

        // Stored-submission cap, deliberately checked only after validation
        // succeeds so that invalid submissions never consume this bucket.
        if (! $this->attempt("contact:{$ip}", 5)) {
            throw new ApiRateLimitedException(
                'You have sent several enquiries already. Please call us on 9879991799 and we will help right away.'
            );
        }

        $attributes['ip_address'] = $ip;
        $attributes['user_agent'] = mb_substr((string) $request->userAgent(), 0, 512);
        $attributes['status'] = 'new';

        $submission = ContactSubmission::query()->create($attributes);

        app(NotifyContactSubmission::class)->handle($submission);

        return ['submissionId' => $submission->id];
    }

    /**
     * @return array<string, mixed>
     */
    private function validate(Request $request): array
    {
        $missing = [];
        foreach (['name', 'phone', 'email', 'service', 'message'] as $field) {
            if (empty($request->input($field))) {
                $missing[] = $field;
            }
        }

        if ($missing !== []) {
            throw new ApiValidationException('Missing required fields: '.implode(', ', $missing));
        }

        $name = trim((string) $request->input('name'));
        if (mb_strlen($name) < 2 || mb_strlen($name) > 255) {
            throw new ApiValidationException('Please enter your name');
        }

        $email = trim((string) $request->input('email'));
        if (filter_var($email, FILTER_VALIDATE_EMAIL) === false || mb_strlen($email) > 254) {
            throw new ApiValidationException('Invalid email address');
        }

        $phone = preg_replace('/\D+/', '', (string) $request->input('phone'));
        $phone = preg_replace('/^(?:91|0)(?=\d{10}$)/', '', (string) $phone);
        if (strlen((string) $phone) !== 10) {
            throw new ApiValidationException('Phone must be 10 digits');
        }

        $message = trim((string) $request->input('message'));
        if (mb_strlen($message) < 1 || mb_strlen($message) > 5000) {
            throw new ApiValidationException('Message must be under 5000 characters');
        }

        $service = ContactService::tryFrom((string) $request->input('service'));
        if ($service === null) {
            throw new ApiValidationException('Invalid service selected');
        }

        $budgetRaw = trim((string) $request->input('budget', ''));
        $budget = null;
        if ($budgetRaw !== '') {
            $budget = ContactBudget::tryFrom($budgetRaw);
            if ($budget === null) {
                throw new ApiValidationException('Invalid budget range selected');
            }
        }

        $eventDateRaw = trim((string) $request->input('event-date', ''));
        $eventDate = null;
        if ($eventDateRaw !== '') {
            try {
                $parsed = Carbon::createFromFormat('Y-m-d', $eventDateRaw);
            } catch (InvalidFormatException) {
                throw new ApiValidationException('Invalid event date');
            }

            if ($parsed->format('Y-m-d') !== $eventDateRaw) {
                throw new ApiValidationException('Invalid event date');
            }
            $eventDate = $eventDateRaw;
        }

        return [
            'name' => $name,
            'phone' => $phone,
            'email' => $email,
            'service' => $service->value,
            'message' => $message,
            'budget' => $budget?->value,
            'event_date' => $eventDate,
        ];
    }
}
