<?php
declare(strict_types=1);

namespace Celena\Plugin\Leads\Controllers;

use Celena\Core\Database\Connection;
use Celena\Core\Http\Request;
use Celena\Core\Http\Response;
use Celena\Core\Mail\Mailer;
use Celena\Core\Security\Csrf;
use Celena\Module\Settings\Options;
use Celena\Plugin\Leads\Repositories\FormRepository;
use Celena\Plugin\Leads\Support\FormNotifier;

/**
 * Общий приём заявок конструктора форм: honeypot → CSRF → динамическая валидация
 * по схеме формы → сохранение JSON → уведомления. Контракт ответа {ok:true}
 * сохранён, поэтому landing.js по-прежнему шлёт gtag-конверсию + celenaTrack.
 */
final class FormSubmitService
{
    public function __construct(
        private readonly Connection $conn,
        private readonly Mailer $mailer,
        private readonly Options $options,
    ) {
    }

    /** @param ?string $slug null → дефолтная форма (контакт-форма лендинга). */
    public function handle(Request $req, ?string $slug): Response
    {
        // 1. Honeypot
        if (trim((string) $req->input('website_url', '')) !== '') {
            return Response::json(['ok' => true]);
        }

        // 2. CSRF (lenient — публичная форма)
        $sessionToken = Csrf::token();
        if ($sessionToken !== '') {
            $token = $req->header('x-csrf-token') ?? (string) $req->input('_csrf', '');
            if (!Csrf::validate($token)) {
                return Response::json(['ok' => false, 'error' => 'csrf'], 419);
            }
        }

        // 3. Форма
        $repo = new FormRepository($this->conn);
        $form = $slug !== null && $slug !== '' ? $repo->findBySlug($slug) : $repo->default();
        if ($form === null) {
            return Response::json(['ok' => false, 'error' => 'form'], 404);
        }
        $fields = is_array($form['fields'] ?? null) ? $form['fields'] : [];

        // 4. Динамическая валидация по схеме
        $data = [];
        $rows = [];
        foreach ($fields as $f) {
            if (!is_array($f)) continue;
            $key  = (string) ($f['key'] ?? '');
            $type = (string) ($f['type'] ?? 'text');
            if ($key === '') continue;
            $required = !empty($f['required']);
            $label = (string) ($f['label'] ?? $key);

            if ($type === 'checkboxes') {
                $raw = $req->input($key, []);
                $vals = is_array($raw) ? array_map(fn ($x) => $this->clean((string) $x, 200), $raw) : [];
                if ($required && $vals === []) {
                    return Response::json(['ok' => false, 'error' => 'required'], 422);
                }
                $data[$key] = $vals;
                $rows[$label] = implode(', ', $vals);
                continue;
            }
            if ($type === 'checkbox' || $type === 'consent') {
                $on = trim((string) $req->input($key, '')) !== '';
                if ($required && !$on) {
                    return Response::json(['ok' => false, 'error' => 'required'], 422);
                }
                $data[$key] = $on ? '1' : '';
                $rows[$label] = $on ? 'да' : 'нет';
                continue;
            }

            $val = $this->clean((string) $req->input($key, ''), $type === 'textarea' ? 4000 : 500);
            if ($type === 'email' && $val !== '') {
                if (!filter_var($val, FILTER_VALIDATE_EMAIL)) {
                    if ($required) {
                        return Response::json(['ok' => false, 'error' => 'email'], 422);
                    }
                    $val = '';
                }
            }
            if ($type === 'tel' && $val !== '') {
                $val = mb_substr($val, 0, 20);
                $digits = preg_replace('/\D+/', '', $val) ?? '';
                // Только +, цифры, пробелы, скобки, дефисы; 7–15 цифр (междунар. формат).
                if (!preg_match('/^\+?[0-9 ()\-]{7,20}$/', $val) || strlen($digits) < 7 || strlen($digits) > 15) {
                    return Response::json(['ok' => false, 'error' => 'phone'], 422);
                }
            }
            if (($type === 'select' || $type === 'radio') && $val !== '') {
                if (!in_array($val, $this->optionValues($f), true)) {
                    $val = '';
                }
            }
            if ($required && $val === '') {
                return Response::json(['ok' => false, 'error' => 'required'], 422);
            }
            $data[$key] = $val;
            $rows[$label] = $val;
        }

        // 5. Сохранение
        try {
            $repo->insertSubmission((int) $form['id'], (string) $form['slug'], $data, [
                'ip'         => $req->ip(),
                'user_agent' => mb_substr((string) ($req->header('user-agent') ?? ''), 0, 255),
                'page_url'   => mb_substr((string) ($req->header('referer') ?? ''), 0, 255),
                'lang'       => $this->clean((string) $req->input('lang', ''), 8),
            ]);
        } catch (\Throwable) {
            return Response::json(['ok' => false, 'error' => 'server'], 500);
        }

        // 6. Уведомления — best-effort
        try {
            (new FormNotifier($this->mailer, $this->options))->notify($form, $rows);
        } catch (\Throwable) {
        }

        return Response::json(['ok' => true]);
    }

    /** @return array<int,string> */
    private function optionValues(array $f): array
    {
        $out = [];
        foreach ((array) ($f['options'] ?? []) as $o) {
            $out[] = is_array($o) ? (string) ($o['value'] ?? '') : (string) $o;
        }
        return $out;
    }

    private function clean(string $value, int $max): string
    {
        return mb_substr(trim(strip_tags($value)), 0, $max);
    }
}
