<?php
declare(strict_types=1);

namespace Celena\Plugin\Leads\Repositories;

use Celena\Core\Database\Connection;

/**
 * Доступ к формам конструктора (cl_forms) и заявкам (cl_form_submissions).
 * Единая точка JSON encode/decode: PDO на pgsql отдаёт JSONB строкой, поэтому
 * на чтении нормализуем через is_array() ? : json_decode().
 */
final class FormRepository
{
    public function __construct(private readonly Connection $conn)
    {
    }

    /** @return array<int, array<string, mixed>> */
    public function all(): array
    {
        $rows = $this->conn->builder('forms')->orderBy('id', 'asc')->get();
        return array_map([$this, 'hydrate'], $rows);
    }

    public function find(int $id): ?array
    {
        $row = $this->conn->builder('forms')->where('id', '=', $id)->first();
        return $row === null ? null : $this->hydrate($row);
    }

    public function findBySlug(string $slug): ?array
    {
        $row = $this->conn->builder('forms')->where('slug', '=', $slug)->first();
        return $row === null ? null : $this->hydrate($row);
    }

    public function default(): ?array
    {
        foreach ($this->all() as $f) {
            if (!empty($f['settings']['is_default'])) {
                return $f;
            }
        }
        return $this->findBySlug('contact');
    }

    /** @param array<string,mixed> $fields @param array<string,mixed> $settings */
    public function create(string $slug, string $title, array $fields, array $settings): string
    {
        $now = date('Y-m-d H:i:s');
        return $this->conn->builder('forms')->insert([
            'slug'       => $slug,
            'title'      => $title,
            'fields'     => $this->enc($fields),
            'settings'   => $this->enc($settings),
            'status'     => 'active',
            'created_at' => $now,
            'updated_at' => $now,
        ]);
    }

    /** @param array<string,mixed> $fields @param array<string,mixed> $settings */
    public function update(int $id, string $title, array $fields, array $settings): void
    {
        $this->conn->builder('forms')->where('id', '=', $id)->update([
            'title'      => $title,
            'fields'     => $this->enc($fields),
            'settings'   => $this->enc($settings),
            'updated_at' => date('Y-m-d H:i:s'),
        ]);
    }

    public function delete(int $id): void
    {
        $this->conn->builder('forms')->where('id', '=', $id)->delete();
    }

    public function uniqueSlug(string $base, int $ignoreId = 0): string
    {
        $base = $base !== '' ? $base : 'form';
        $slug = $base;
        $i = 2;
        while (true) {
            $row = $this->conn->builder('forms')->where('slug', '=', $slug)->first();
            if ($row === null || (int) $row['id'] === $ignoreId) {
                return $slug;
            }
            $slug = $base . '-' . $i;
            $i++;
        }
    }

    // ── Заявки ──

    /** @param array<string,mixed> $data */
    public function insertSubmission(int $formId, string $formSlug, array $data, array $meta): string
    {
        return $this->conn->builder('form_submissions')->insert([
            'form_id'    => $formId ?: null,
            'form_slug'  => $formSlug,
            'data'       => $this->enc($data),
            'ip'         => $meta['ip'] ?? null,
            'user_agent' => $meta['user_agent'] ?? null,
            'page_url'   => $meta['page_url'] ?? null,
            'lang'       => $meta['lang'] ?? null,
            'status'     => 'new',
            'created_at' => date('Y-m-d H:i:s'),
        ]);
    }

    /** @return array<int, array<string,mixed>> */
    public function submissions(int $formId, string $status = '', int $limit = 50, int $offset = 0): array
    {
        $q = $this->conn->builder('form_submissions')->where('form_id', '=', $formId);
        if ($status !== '') {
            $q->where('status', '=', $status);
        }
        $rows = $q->orderBy('id', 'desc')->limit($limit)->offset($offset)->get();
        foreach ($rows as &$r) {
            $r['data'] = $this->dec($r['data'] ?? null);
        }
        unset($r);
        return $rows;
    }

    public function submissionsCount(int $formId, string $status = ''): int
    {
        $q = $this->conn->builder('form_submissions')->where('form_id', '=', $formId);
        if ($status !== '') {
            $q->where('status', '=', $status);
        }
        return $q->count();
    }

    /**
     * Заявки всех форм скопом — для объединённого инбокса «Все заявки» (/admin/leads).
     * Объём лид-формы небольшой, поэтому отдаём всё; пагинация делается на стороне вызова.
     * @return array<int, array<string,mixed>>
     */
    public function allSubmissions(string $status = ''): array
    {
        $q = $this->conn->builder('form_submissions');
        if ($status !== '') {
            $q->where('status', '=', $status);
        }
        $rows = $q->orderBy('id', 'desc')->get();
        foreach ($rows as &$r) {
            $r['data'] = $this->dec($r['data'] ?? null);
        }
        unset($r);
        return $rows;
    }

    public function newCountAll(): int
    {
        return $this->conn->builder('form_submissions')->where('status', '=', 'new')->count();
    }

    public function setSubmissionStatus(int $id, string $status): void
    {
        $this->conn->builder('form_submissions')->where('id', '=', $id)->update(['status' => $status]);
    }

    public function deleteSubmission(int $id): void
    {
        $this->conn->builder('form_submissions')->where('id', '=', $id)->delete();
    }

    // ── helpers ──

    private function hydrate(array $row): array
    {
        $row['fields'] = $this->dec($row['fields'] ?? null);
        $row['settings'] = $this->dec($row['settings'] ?? null);
        return $row;
    }

    private function enc(mixed $v): string
    {
        return json_encode($v, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?: '[]';
    }

    private function dec(mixed $v): array
    {
        if (is_array($v)) {
            return $v;
        }
        $d = json_decode((string) $v, true);
        return is_array($d) ? $d : [];
    }
}
