<?php

declare(strict_types=1);

// This copy-ready adapter stays outside PhpWA core. EasyChat owns its channel
// rows and generations; PhpWA owns protocol keys, ratchets and its outbox.

use PhpWA\Client;
use PhpWA\Config\ClientOptions;
use PhpWA\Integration\GenerationRunGuard;
use PhpWA\Runtime\PrivateFileAccountLockProvider;
use PhpWA\Runtime\RunCheckpoint;
use PhpWA\Storage\AesGcmKeyringCipher;
use PhpWA\Storage\PdoAccountStore;

interface EasyChatPhpWaGenerationRepository
{
    /** Must use a bounded indexed lookup. */
    public function currentGeneration(int $channelId): int;

    /** Must update only when channel + generation still match. */
    public function heartbeat(int $channelId, int $generation, RunCheckpoint $checkpoint): void;
}

/**
 * Schema installation belongs in an EasyChat migration, not in this factory:
 * PdoSchema::install($pdo, 'phpwa_');
 */
function createEasyChatPhpWaClient(
    PDO $pdo,
    EasyChatPhpWaGenerationRepository $generations,
    int $channelId,
    int $generation,
    string $privateRoot,
    string $storageMasterKey,
    string $indexKey
): Client {
    if ($channelId < 1 || $generation < 1) {
        throw new InvalidArgumentException('EasyChat channel and generation must be positive.');
    }
    if ($privateRoot === '' || $privateRoot[0] !== DIRECTORY_SEPARATOR) {
        throw new InvalidArgumentException('EasyChat PhpWA root must be an absolute private path.');
    }
    if (strlen($storageMasterKey) !== 32 || strlen($indexKey) !== 32
        || hash_equals($storageMasterKey, $indexKey)) {
        throw new InvalidArgumentException('PhpWA requires two independent 32-byte storage keys.');
    }

    // Keep this logical ID stable for the lifetime of the EasyChat channel.
    // It scopes the shared worker lock, dedupe and deterministic outbox event
    // IDs. The generation cancels stale processes; it must not fork protocol
    // identity or make a redelivered WhatsApp message look new.
    $accountId = 'easychat:channel:' . $channelId;
    $root = rtrim($privateRoot, DIRECTORY_SEPARATOR);
    $workDirectory = $root . DIRECTORY_SEPARATOR . 'channel-' . $channelId
        . DIRECTORY_SEPARATOR . 'generation-' . $generation;
    $store = new PdoAccountStore(
        $pdo,
        new AesGcmKeyringCipher('primary', ['primary' => $storageMasterKey]),
        $indexKey,
        'phpwa_'
    );
    $locks = new PrivateFileAccountLockProvider($root . DIRECTORY_SEPARATOR . 'locks');
    $guard = new GenerationRunGuard(
        $generation,
        static function () use ($generations, $channelId): int {
            return $generations->currentGeneration($channelId);
        },
        static function (RunCheckpoint $checkpoint) use ($generations, $channelId, $generation): void {
            $generations->heartbeat($channelId, $generation, $checkpoint);
        }
    );

    $options = new ClientOptions(
        $accountId,
        $workDirectory,
        null,
        $store,
        $locks,
        null,
        null,
        15.0,
        268435456
    );
    return new Client($options->withRunGuard($guard, 1.0));
}
