<?php

declare(strict_types=1);

namespace PhpWA\Config;

use InvalidArgumentException;
use PhpWA\Auth\CompanionDeviceConfig;
use PhpWA\Runtime\AccountLockProviderInterface;
use PhpWA\Runtime\RunGuardInterface;
use PhpWA\Storage\AccountStoreInterface;
use PhpWA\Transport\WebSocket\WebSocketClientOptions;

final class ClientOptions
{
      private $accountId;
      private $workDirectory;
      private $storageKey;
      private $accountStore;
      private $lockProvider;
      private $companion;
      private $webSocket;
      private $operationTimeout;
      private $maximumMediaBytes;
      private $connectionFactory;
      private $runGuard;
      private $runGuardInterval;

    






    public function __construct(
        string $accountId,
        string $workDirectory,
        ?string $storageKey = null,
        ?AccountStoreInterface $accountStore = null,
        ?AccountLockProviderInterface $lockProvider = null,
        ?CompanionDeviceConfig $companion = null,
        ?WebSocketClientOptions $webSocket = null,
        float $operationTimeout = 15.0,
        int $maximumMediaBytes = 268435456,
        ?callable $connectionFactory = null,
        ?RunGuardInterface $runGuard = null,
        float $runGuardInterval = 1.0
    ) {
        if ($accountId === '' || strlen($accountId) > 512 || preg_match('//u', $accountId) !== 1
            || preg_match('/[\x00-\x1f\x7f]/', $accountId) === 1) {
            throw new InvalidArgumentException('PhpWA account ID is invalid.');
        }
        if ($workDirectory === '' || $workDirectory[0] !== "\x2f" || strpos($workDirectory, "\0") !== false) {
            throw new InvalidArgumentException('PhpWA work directory must be an absolute path.');
        }
        if ($accountStore === null && (!is_string($storageKey) || strlen($storageKey) !== 32)) {
            throw new InvalidArgumentException('The encrypted file store requires a raw 32-byte storage key.');
        }
        if ($storageKey !== null && strlen($storageKey) !== 32) {
            throw new InvalidArgumentException('PhpWA storage key must contain exactly 32 bytes.');
        }
        if (!is_finite($operationTimeout) || $operationTimeout < 0.1 || $operationTimeout > 300.0
            || $maximumMediaBytes < 1 || $maximumMediaBytes > 2147483647
            || !is_finite($runGuardInterval) || $runGuardInterval < 0.1 || $runGuardInterval > 5.0) {
            throw new InvalidArgumentException('PhpWA operation limits are invalid.');
        }
        $this->accountId = $accountId; $this->workDirectory = rtrim($workDirectory, DIRECTORY_SEPARATOR);
        $this->storageKey = $storageKey; $this->accountStore = $accountStore; $this->lockProvider = $lockProvider;
        $this->companion = $companion ?? new CompanionDeviceConfig();
        $this->webSocket = $webSocket ?? new WebSocketClientOptions();
        $this->operationTimeout = $operationTimeout; $this->maximumMediaBytes = $maximumMediaBytes;
        $this->connectionFactory = $connectionFactory;
        $this->runGuard = $runGuard; $this->runGuardInterval = $runGuardInterval;
    }

    public function accountId(): string { return $this->accountId; }
    public function workDirectory(): string { return $this->workDirectory; }
    public function storageKey(): ?string { return $this->storageKey; }
    public function accountStore(): ?AccountStoreInterface { return $this->accountStore; }
    public function lockProvider(): ?AccountLockProviderInterface { return $this->lockProvider; }
    public function companion(): CompanionDeviceConfig { return $this->companion; }
    public function webSocket(): WebSocketClientOptions { return $this->webSocket; }
    public function operationTimeout(): float { return $this->operationTimeout; }
    public function maximumMediaBytes(): int { return $this->maximumMediaBytes; }
    public function connectionFactory(): ?callable { return $this->connectionFactory; }
    public function runGuard(): ?RunGuardInterface { return $this->runGuard; }
    public function runGuardInterval(): float { return $this->runGuardInterval; }

     
    public function withRunGuard(RunGuardInterface $runGuard, float $interval = 1.0): self
    {
        if (!is_finite($interval) || $interval < 0.1 || $interval > 5.0) {
            throw new InvalidArgumentException('Run guard interval must be between 0.1 and 5 seconds.');
        }
        $copy = clone $this;
        $copy->runGuard = $runGuard;
        $copy->runGuardInterval = $interval;
        return $copy;
    }
}
