<?php

namespace App\Services\AI;

use App\Contracts\VoiceSynthesizer;
use App\Exceptions\ProviderNotConfiguredException;
use App\Models\Voice;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use RuntimeException;

class LaravelAiVoiceSynthesizer implements VoiceSynthesizer
{
    public function synthesize(string $text, Voice $voice): string
    {
        return $this->generate($text, $voice, 'creatives/audio');
    }

    public function preview(Voice $voice, string $sampleText = 'Hi, this is a preview of my voice.'): string
    {
        return $this->generate($sampleText, $voice, 'voices/previews');
    }

    private function generate(string $text, Voice $voice, string $directory): string
    {
        if ($voice->provider !== 'gemini') {
            throw new RuntimeException('Only Gemini voices can be previewed.');
        }

        $apiKey = config('ai.providers.gemini.key');
        if (blank($apiKey)) {
            throw new ProviderNotConfiguredException('GEMINI_API_KEY is required for voice generation.');
        }

        $payload = [
            'model' => config('ai.providers.gemini.models.audio.default'),
            'input' => [[
                'type' => 'user_input',
                'content' => [[
                    'type' => 'text',
                    'text' => $text,
                    'annotations' => [[
                        'type' => 'speech_metadata',
                        'style' => trim(implode(' ', array_filter([
                            $voice->tone ? "{$voice->tone} delivery" : null,
                            $voice->accent ? "with a {$voice->accent} accent" : null,
                        ]))) ?: 'natural conversational delivery',
                    ]],
                ]],
            ]],
            'response_format' => ['type' => 'audio'],
            'generation_config' => ['speech_config' => [['voice' => $voice->provider_voice_id]]],
        ];

        $response = Http::withHeaders(['x-goog-api-key' => $apiKey])
            ->timeout(120)
            ->post(rtrim(config('ai.providers.gemini.url'), '/').'/interactions', $payload);

        if ($response->failed()) {
            throw new RuntimeException('Gemini voice generation failed.');
        }

        $audio = collect($response->json('steps', []))
            ->filter(fn (mixed $step): bool => is_array($step) && ($step['type'] ?? null) === 'model_output')
            ->flatMap(fn (array $step): array => $step['content'] ?? [])
            ->filter(fn (mixed $content): bool => is_array($content) && ($content['type'] ?? null) === 'audio')
            ->last();
        $bytes = is_array($audio) ? base64_decode((string) ($audio['data'] ?? ''), true) : false;

        if ($bytes === false || strlen($bytes) < 12 || ! str_starts_with($bytes, 'RIFF') || substr($bytes, 8, 4) !== 'WAVE') {
            throw new RuntimeException('Gemini did not return a valid WAV voice sample.');
        }

        $path = $directory.'/'.Str::ulid().'.wav';
        if (! Storage::disk('public')->put($path, $bytes)) {
            throw new RuntimeException('Failed to store the generated audio.');
        }

        return $path;
    }
}
