<?php

enum LLMProviderType: string
{
    case OPENAI = 'openai';
    case ANTHROPIC = 'anthropic';
    case GEMINI = 'gemini';
    case MISTRAL = 'mistral';
    case OLLAMA = 'ollama';
    case OPEANAILIKE = 'openai_like';
}

final class LLMConfigEntity implements JsonSerializable
{
    public function __construct(
        public readonly ?int $id = null,
        public LLMProviderType $provider,
        public string $model,
        public ?string $token,
        public ?string $base_url = null,
        public ?string $name = null,
        public ?string $description = null,
        public array $meta = [],
        public readonly ?string $created_at = null,
        public ?string $updated_at = null,
    ) {
    }

    public function toArray(bool $showToken = false): array
    {
        return [
            'id' => $this->id,
            'provider' => $this->provider->value,
            'model' => $this->model,
            'token' => $showToken ? $this->token : ($this->token ? '(token is set)' : ''),
            'base_url' => $this->base_url,
            'name' => $this->name,
            'description' => $this->description,
            'meta' => json_encode($this->meta),
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }

    public static function fromArray(array $data): self
    {
        return new self(
            id: $data['id'] ?? null,
            provider: LLMProviderType::from($data['provider']),
            model: $data['model'],
            token: $data['token'] ?? null,
            base_url: $data['base_url'] ?? null,
            name: $data['name'] ?? null,
            description: $data['description'] ?? null,
            meta: json_decode($data['meta'] ?? '{}', true),
            created_at: $data['created_at'] ?? null,
            updated_at: $data['updated_at'] ?? null,
        );
    }
    /**
     * When object will pass to json_encode() it will use toArray() without showing token
     */
    public function jsonSerialize(): array
    {
        return $this->toArray(showToken: false);
    }
}

class CfLLMConfig
{
    public const TABLE = 'llm_configurations';

    public function __construct(
        public readonly PDO $db
    ) {
    }

    /**
     * @return array<LLMConfigEntity>
     */
    public function list(): array
    {
        $stmt = $this->db->prepare("
            SELECT id, provider, model, base_url, token, name, description, meta, created_at, updated_at
            FROM " . self::TABLE . "
            ORDER BY created_at DESC
        ");
        $stmt->execute();
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        return array_map(
            fn (array $row) => LLMConfigEntity::fromArray($row),
            $rows
        );
    }

    public function create(LLMConfigEntity $config): int
    {
        $stmt = $this->db->prepare("
            INSERT INTO " . self::TABLE . " 
            (provider, model, token, base_url, name, description, meta, created_at, updated_at)
            VALUES (:provider, :model, :token, :base_url, :name, :description, :meta, NOW(), NOW())
        ");

        $stmt->execute([
            'provider' => $config->provider->value,
            'model' => $config->model,
            'token' => $config->token,
            'base_url' => $config->base_url,
            'name' => $config->name,
            'description' => $config->description,
            'meta' => json_encode($config->meta)
        ]);

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

    public function update(int $id, LLMConfigEntity $config): bool
    {
        $stmt = $this->db->prepare("
            UPDATE " . self::TABLE . "
            SET provider = :provider,
                model = :model,
                token = :token,
                base_url = :base_url,
                name = :name,
                description = :description,
                meta = :meta,
                updated_at = NOW()
            WHERE id = :id
        ");

        $result = $stmt->execute([
            'id' => $id,
            'provider' => $config->provider->value,
            'model' => $config->model,
            'token' => $config->token,
            'base_url' => $config->base_url,
            'name' => $config->name,
            'description' => $config->description,
            'meta' => json_encode($config->meta)
        ]);

        return $result && $stmt->rowCount() > 0;
    }

    public function delete(int $id): bool
    {
        $stmt = $this->db->prepare("
            DELETE FROM " . self::TABLE . "
            WHERE id = :id
        ");

        $result = $stmt->execute(['id' => $id]);
        return $result && $stmt->rowCount() > 0;
    }

    public function get(int $id): ?LLMConfigEntity
    {
        $stmt = $this->db->prepare("
            SELECT id, provider, model, token, base_url, name, description, meta, created_at, updated_at
            FROM " . self::TABLE . "
            WHERE id = :id
        ");

        $stmt->execute(['id' => $id]);
        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        return $row ? LLMConfigEntity::fromArray($row) : null;
    }
}
