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

declare(strict_types=1);

// Manual harness: runs the FULL pipeline (extraction + editorial generation)
// on a JSONL corpus and prints the final report JSON. Requires GCP
// credentials (GCP_PROJECT_ID, GCP_REGION, application default credentials).
// Shares the extraction cache with bin/review-synth-eval.
// Usage: bin/review-synth-report --profile=game --score=19 --reviews=tests/Fixtures/game/ff7/reviews.jsonl
//        [--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\Aggregator\Aggregator;
use Webedia\AiBundle\ReviewSynth\Aggregator\SentimentResolver;
use Webedia\AiBundle\ReviewSynth\Contract\Review;
use Webedia\AiBundle\ReviewSynth\Extractor\SchemaBuilder;
use Webedia\AiBundle\ReviewSynth\Extractor\Extractor;
use Webedia\AiBundle\ReviewSynth\Generator\EditorialWriter;
use Webedia\AiBundle\ReviewSynth\Generator\Generator;
use Webedia\AiBundle\ReviewSynth\Generator\ReportAssembler;
use Webedia\AiBundle\ReviewSynth\GeminiClient;
use Webedia\AiBundle\ReviewSynth\ProfileRegistry;
use Webedia\AiBundle\ReviewSynth\PromptRepository;
use Webedia\AiBundle\ReviewSynth\ReviewSynthesizer;
use Webedia\AiBundle\ReviewSynth\Generator\RuleValidator;

$options = getopt('', ['profile:', 'model::', 'reviews:', 'score:', 'batch-size::', 'concurrency::']);
$profileName = (string) ($options['profile'] ?? 'game');
$model = '' !== (string) ($options['model'] ?? '') ? (string) $options['model'] : null;
$reviewsFile = (string) ($options['reviews'] ?? '');
$score = (float) str_replace(',', '.', (string) ($options['score'] ?? ''));
if ('' === $reviewsFile || !isset($options['score'])) {
    fwrite(STDERR, "--reviews and --score are required.\n");
    exit(1);
}

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

$bundleDir = dirname(__DIR__);
$rows = [];
foreach (file($reviewsFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [] as $line) {
    $rows[] = json_decode($line, true, 512, JSON_THROW_ON_ERROR);
}
$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,
    ),
    $rows,
);

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

$llm = new GeminiClient(
    (string) getenv('GCP_PROJECT_ID'),
    (string) getenv('GCP_REGION'),
    maxConcurrency: max(1, (int) ($options['concurrency'] ?? 10)),
    progress: $progress,
);
$prompts = new PromptRepository($bundleDir);
$extractionCache = new FilesystemAdapter('review_synth_eval', 0, "$bundleDir/var/cache");

$synthesizer = new ReviewSynthesizer(
    new ProfileRegistry([$profileName => Yaml::parseFile("$bundleDir/profiles/$profileName.yaml")]),
    new Extractor($llm, $extractionCache, $prompts, new SchemaBuilder(), max(1, (int) ($options['batch-size'] ?? 20)), $progress),
    new Aggregator(new SentimentResolver()),
    new Generator(new EditorialWriter($llm, $prompts), new RuleValidator(), new ReportAssembler()),
    new FilesystemAdapter('review_synth_report', 0, "$bundleDir/var/cache"),
    $prompts,
    'gemini-2.5-flash',
);

fwrite(STDERR, sprintf("Synthesizing %d reviews (profile %s, model %s)...\n", count($reviews), $profileName, $model ?? 'gemini-2.5-flash'));
$start = microtime(true);
$result = $synthesizer->synthesize($profileName, $reviews, $score, $model);
fwrite(STDERR, sprintf("Done in %.1fs.\n", microtime(true) - $start));

echo json_encode($result->report->toArray(), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR)."\n";
