#!/usr/bin/env php
<?php

declare(strict_types=1);

// Manual evaluation harness: runs the REAL Gemini extraction on the fixture
// corpus and prints per-component precision/recall. Requires GCP credentials
// (GCP_PROJECT_ID, GCP_REGION, application default credentials).
// Usage: bin/review-synth-eval --profile=game --model=gemini-2.5-flash
//        [--batch-size=20] [--concurrency=10]

require __DIR__.'/../vendor/autoload.php';

use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\Yaml\Yaml;
use Webedia\AiBundle\ReviewSynth\Contract\Review;
use Webedia\AiBundle\ReviewSynth\Extractor\ExtractionEvaluator;
use Webedia\AiBundle\ReviewSynth\Extractor\SchemaBuilder;
use Webedia\AiBundle\ReviewSynth\Extractor\Extractor;
use Webedia\AiBundle\ReviewSynth\GeminiClient;
use Webedia\AiBundle\ReviewSynth\ProfileFactory;
use Webedia\AiBundle\ReviewSynth\PromptRepository;

$options = getopt('', ['profile:', 'model:', 'reviews::', 'annotations::', 'batch-size::', 'concurrency::']);
$profileName = (string) ($options['profile'] ?? 'game');
$model = (string) ($options['model'] ?? '');
if ('' === $model) {
    fwrite(STDERR, "--model is required (e.g. gemini-2.5-flash)\n");
    exit(1);
}
$bundleDir = dirname(__DIR__);
$reviewsFile = (string) ($options['reviews'] ?? "$bundleDir/tests/Fixtures/$profileName/reviews.jsonl");
$annotationsFile = (string) ($options['annotations'] ?? "$bundleDir/tests/Fixtures/$profileName/annotations.jsonl");

foreach (['GCP_PROJECT_ID', 'GCP_REGION'] as $env) {
    if (false === getenv($env) || '' === getenv($env)) {
        fwrite(STDERR, "Missing env var $env.\n");
        exit(1);
    }
}

$profile = ProfileFactory::fromArray($profileName, Yaml::parseFile("$bundleDir/profiles/$profileName.yaml"));

$readJsonl = static function (string $file): array {
    $rows = [];
    foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
        $rows[] = json_decode($line, true, 512, JSON_THROW_ON_ERROR);
    }

    return $rows;
};

$reviews = array_map(
    static fn (array $row): Review => Review::create(
        (string) $row['text'],
        isset($row['rating']) ? (float) $row['rating'] : null,
        isset($row['user_level']) ? (int) $row['user_level'] : null,
    ),
    $readJsonl($reviewsFile),
);
$annotations = $readJsonl($annotationsFile);

$progress = static function (string $message): void {
    fwrite(STDERR, "  $message\n");
};

$extractor = new Extractor(
    new GeminiClient(
        (string) getenv('GCP_PROJECT_ID'),
        (string) getenv('GCP_REGION'),
        maxConcurrency: max(1, (int) ($options['concurrency'] ?? 10)),
        progress: $progress,
    ),
    new FilesystemAdapter('review_synth_eval', 0, "$bundleDir/var/cache"),
    new PromptRepository($bundleDir),
    new SchemaBuilder(),
    max(1, (int) ($options['batch-size'] ?? 20)),
    $progress,
);

fwrite(STDERR, sprintf("Extracting %d reviews with %s...\n", count($reviews), $model));
$start = microtime(true);
$extractions = $extractor->extract($profile, $reviews, $model);
fwrite(STDERR, sprintf("Done in %.1fs (cache hits make re-runs near-instant).\n", microtime(true) - $start));

$metrics = (new ExtractionEvaluator())->evaluate($extractions, $annotations);

printf("%-18s %10s %10s %8s %10s\n", 'component', 'precision', 'recall', 'support', 'pol.acc');
foreach ($metrics as $component => $m) {
    if ('_rejection' === $component) {
        continue;
    }
    printf(
        "%-18s %9.1f%% %9.1f%% %8d %9s\n",
        $component,
        100 * $m['precision'],
        100 * $m['recall'],
        $m['support'],
        null === $m['polarity_accuracy'] ? 'n/a' : sprintf('%.1f%%', 100 * $m['polarity_accuracy']),
    );
}
if (isset($metrics['_rejection'])) {
    printf("%-18s %9.1f%%\n", 'rejection acc.', 100 * $metrics['_rejection']['accuracy']);
}
