<?php
declare(strict_types=1);

namespace App\Modules\Sms;

use PDO;

final class SmsMessageRepository
{
    public function __construct(private readonly PDO $pdo)
    {
    }

    /**
     * @param array<string, mixed> $data
     */
    public function insert(array $data): int
    {
        $statement = $this->pdo->prepare(
            'INSERT INTO sms_messages (
                direction, provider, order_id, from_phone, from_phone_normalized,
                to_phone, to_phone_normalized, body, message_id, status,
                raw_payload_json, created_by, created_at, updated_at
            ) VALUES (
                :direction, :provider, :order_id, :from_phone, :from_phone_normalized,
                :to_phone, :to_phone_normalized, :body, :message_id, :status,
                :raw_payload_json, :created_by, NOW(), NOW()
            )'
        );
        $statement->execute([
            'direction' => (string) $data['direction'],
            'provider' => (string) ($data['provider'] ?? 'smsplanet'),
            'order_id' => $this->nullableInt($data['order_id'] ?? null),
            'from_phone' => (string) $data['from_phone'],
            'from_phone_normalized' => (string) $data['from_phone_normalized'],
            'to_phone' => (string) $data['to_phone'],
            'to_phone_normalized' => (string) $data['to_phone_normalized'],
            'body' => (string) $data['body'],
            'message_id' => $this->nullableString((string) ($data['message_id'] ?? '')),
            'status' => substr((string) ($data['status'] ?? 'received'), 0, 32),
            'raw_payload_json' => $this->jsonOrNull($data['raw_payload'] ?? null),
            'created_by' => $this->nullableInt($data['created_by'] ?? null),
        ]);

        return (int) $this->pdo->lastInsertId();
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function findByOrderId(int $orderId): array
    {
        if ($orderId <= 0) {
            return [];
        }

        $statement = $this->pdo->prepare(
            'SELECT *
             FROM sms_messages
             WHERE order_id = :order_id
             ORDER BY created_at ASC, id ASC'
        );
        $statement->execute(['order_id' => $orderId]);
        $rows = $statement->fetchAll(PDO::FETCH_ASSOC);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @param array<int, string> $phones
     */
    public function findLatestOrderIdByPhones(array $phones): ?int
    {
        $variants = array_values(array_unique(array_filter($phones, static fn (string $value): bool => $value !== '')));
        if ($variants === []) {
            return null;
        }

        $addressPlaceholders = [];
        $params = [];
        foreach ($variants as $index => $phone) {
            $addressKey = 'address_phone' . $index;
            $addressPlaceholders[] = ':' . $addressKey;
            $params[$addressKey] = $phone;
        }

        $statement = $this->pdo->prepare(
            'SELECT o.id
             FROM orders o
             WHERE EXISTS (
                 SELECT 1
                 FROM order_addresses a
                 WHERE a.order_id = o.id
                   AND REGEXP_REPLACE(COALESCE(a.phone, ""), "[^0-9]+", "") IN (' . implode(',', $addressPlaceholders) . ')
             )
             ORDER BY COALESCE(o.ordered_at, o.source_created_at, o.created_at) DESC, o.id DESC
             LIMIT 1'
        );
        $statement->execute($params);
        $value = $statement->fetchColumn();
        $orderId = (int) $value;

        return $orderId > 0 ? $orderId : null;
    }

    private function nullableString(string $value): ?string
    {
        $trimmed = trim($value);

        return $trimmed === '' ? null : $trimmed;
    }

    private function nullableInt(mixed $value): ?int
    {
        $intValue = (int) $value;

        return $intValue > 0 ? $intValue : null;
    }

    private function jsonOrNull(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $encoded = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

        return is_string($encoded) ? $encoded : null;
    }
}
