<?php
namespace EasyChat\Library\Messenger;

use EasyChat\Model\Messenger as MessengerModel;





final class SmsInboundContactFilter {
    public const VERSION = 'v1';
    public const TTL_SECONDS = 86400;
    public const MAX_PHONE_HASHES = 20000;

    private $registry;

    public function __construct($registry) {
        $this->registry = $registry;
    }

    public function build(
        string $session_token,
        string $installation_scope,
        string $known_revision = '',
        bool $active = true,
        ?int $now = null
    ): array {
        $key = self::deriveKey($session_token);
        if ($key === '' || !preg_match('/^[a-f0-9]{32}$/D', $installation_scope)) {
            return $this->invalidPayload();
        }

        $hashes = [];
        $complete = true;
        if ($active) {
            $phones = (new MessengerModel($this->registry))->listSmsDialogPhoneNumbers(
                self::MAX_PHONE_HASHES + 1
            );
             
             
            if (count($phones) > self::MAX_PHONE_HASHES) {
                $complete = false;
            } else {
                foreach ($phones as $phone) {
                    foreach (self::fingerprintsForPhone((string)$phone, $key) as $fingerprint) {
                        $hashes[$fingerprint] = true;
                        if (count($hashes) > self::MAX_PHONE_HASHES) {
                            $complete = false;
                            break 2;
                        }
                    }
                }
            }
        }

        $hashes = array_keys($hashes);
        sort($hashes, SORT_STRING);
        if (!$complete) {
             
             
            $hashes = [];
        }

        $revision = self::revision($installation_scope, $active, $complete, $hashes);
        $known_revision = strtolower(trim($known_revision));
        $unchanged = $complete
            && preg_match('/^[a-f0-9]{64}$/D', $known_revision)
            && hash_equals($revision, $known_revision);
        $now = $now ?? time();
        $expires_at_ms = ($now + self::TTL_SECONDS) * 1000;
        $hashes_digest = hash('sha256', implode("\n", $hashes));
        $payload = [
            'version' => self::VERSION,
            'installation_scope' => $installation_scope,
            'revision' => $revision,
            'ttl_seconds' => self::TTL_SECONDS,
            'expires_at_ms' => $expires_at_ms,
            'active' => $active ? 1 : 0,
            'complete' => $complete ? 1 : 0,
            'unchanged' => $unchanged ? 1 : 0,
            'count' => count($hashes),
            'phone_hashes' => $unchanged ? [] : $hashes,
            'hashes_digest' => $hashes_digest,
        ];
        $payload['signature'] = hash_hmac('sha256', self::signatureCanonical($payload), $key);
        return $payload;
    }

     
    public static function phoneVariants(string $phone): array {
        $normalized = SmsPhoneNumber::normalize($phone);
        if ($normalized === '') {
            return [];
        }

        $digits = preg_replace('/\D+/', '', $normalized);
        if (!is_string($digits) || $digits === '') {
            return [];
        }

        $variants = ['full:' . $digits];
         
         
         
        if (strlen($digits) === 11 && $digits[0] === '7') {
            $variants[] = 'national10:' . substr($digits, -10);
        }
        return array_values(array_unique($variants));
    }

     
    public static function fingerprintsForPhone(string $phone, string $key): array {
        if (strlen($key) !== 32) {
            return [];
        }

        return array_map(static function (string $variant) use ($key): string {
            return hash_hmac('sha256', $variant, $key);
        }, self::phoneVariants($phone));
    }

     
    public static function deriveKey(string $session_token): string {
        $session_token = trim($session_token);
        return $session_token === ''
            ? ''
            : hash('sha256', "easychat:sms-inbound-filter:v1\0" . $session_token, true);
    }

    public static function signatureCanonical(array $payload): string {
        return implode("\n", [
            (string)($payload['version'] ?? ''),
            (string)($payload['installation_scope'] ?? ''),
            (string)($payload['revision'] ?? ''),
            (string)(int)($payload['ttl_seconds'] ?? 0),
            (string)(int)($payload['expires_at_ms'] ?? 0),
            !empty($payload['active']) ? '1' : '0',
            !empty($payload['complete']) ? '1' : '0',
            !empty($payload['unchanged']) ? '1' : '0',
            (string)(int)($payload['count'] ?? 0),
            (string)($payload['hashes_digest'] ?? ''),
        ]);
    }

    private static function revision(string $scope, bool $active, bool $complete, array $hashes): string {
        return hash('sha256', implode("\0", [
            self::VERSION,
            $scope,
            $active ? 'active' : 'revoked',
            $complete ? 'complete' : 'incomplete',
            implode("\n", $hashes),
        ]));
    }

    private function invalidPayload(): array {
        return [
            'version' => self::VERSION,
            'installation_scope' => '',
            'revision' => '',
            'ttl_seconds' => 0,
            'expires_at_ms' => 0,
            'active' => 0,
            'complete' => 0,
            'unchanged' => 0,
            'count' => 0,
            'phone_hashes' => [],
            'hashes_digest' => hash('sha256', ''),
            'signature' => '',
        ];
    }
}
