<?php

namespace App\Services\Meet;

use App\Jobs\GenerateAiSummary;
use App\Models\Recording;
use App\Models\Session;
use App\Models\Transcript;

class TranscriptService
{
    public function getOrCreateForSession(Session $session, ?Recording $recording = null): Transcript
    {
        $existing = $session->transcripts()->where('status', '!=', 'failed')->latest()->first();
        if ($existing) {
            if ($recording && ! $existing->recording_id) {
                $existing->update(['recording_id' => $recording->id]);
            }

            return $existing;
        }

        return Transcript::create([
            'owner_ref' => $session->owner_ref,
            'session_id' => $session->id,
            'recording_id' => $recording?->id,
            'status' => 'processing',
            'live_captions_enabled' => (bool) $session->room->setting('live_captions', false),
        ]);
    }

    /** @deprecated Use finalizeAndSummarize() when a recording is ready. */
    public function ensureForSession(Session $session, ?Recording $recording = null): Transcript
    {
        return $this->finalizeAndSummarize($session, $recording);
    }

    public function finalizeAndSummarize(Session $session, ?Recording $recording = null): Transcript
    {
        $transcript = $this->getOrCreateForSession($session, $recording);
        $this->refreshContent($transcript, $session);

        GenerateAiSummary::dispatch($transcript->id);

        return $transcript->fresh();
    }

    public function appendSegment(Transcript $transcript, string $speaker, string $text): Transcript
    {
        $segments = $transcript->segments ?? [];
        $segments[] = [
            'speaker' => $speaker,
            'text' => trim($text),
            'at' => now()->toIso8601String(),
            'source' => 'caption',
        ];

        $segments = $this->compactCaptionSegments($segments);
        $content = $this->segmentsToContent($segments);

        $transcript->update([
            'segments' => $segments,
            'content' => $content,
            'status' => 'ready',
        ]);

        return $transcript;
    }

    public function refreshContent(Transcript $transcript, Session $session): void
    {
        $segments = $session->messages()
            ->orderBy('created_at')
            ->get()
            ->map(fn ($message) => [
                'speaker' => $message->sender_name,
                'text' => $message->body,
                'at' => $message->created_at->toIso8601String(),
                'source' => 'chat',
            ])
            ->all();

        foreach ($transcript->segments ?? [] as $segment) {
            $source = $segment['source'] ?? null;
            if ($source === 'chat') {
                continue;
            }

            $text = trim((string) ($segment['text'] ?? ''));
            if ($text === '') {
                continue;
            }

            $segments[] = array_merge($segment, ['source' => 'caption']);
        }

        $segments = $this->compactCaptionSegments($segments);
        usort($segments, fn (array $a, array $b) => strcmp((string) ($a['at'] ?? ''), (string) ($b['at'] ?? '')));
        $content = $this->segmentsToContent($segments);

        $transcript->update([
            'segments' => $segments,
            'content' => $content !== '' ? $content : 'No transcript content available.',
            'status' => 'ready',
        ]);
    }

    /**
     * @param  array<int, array<string, mixed>>  $segments
     * @return array<int, array<string, mixed>>
     */
    protected function compactCaptionSegments(array $segments): array
    {
        $compact = [];

        foreach ($segments as $segment) {
            if (($segment['source'] ?? '') === 'chat') {
                $compact[] = $segment;

                continue;
            }

            $speaker = (string) ($segment['speaker'] ?? 'Speaker');
            $text = trim((string) ($segment['text'] ?? ''));
            if ($text === '') {
                continue;
            }

            $lastCaptionIndex = $this->lastCaptionIndex($compact);
            if ($lastCaptionIndex !== null && ($compact[$lastCaptionIndex]['speaker'] ?? '') === $speaker) {
                $lastText = trim((string) ($compact[$lastCaptionIndex]['text'] ?? ''));

                if ($this->captionTextsEquivalent($lastText, $text)) {
                    continue;
                }

                if ($lastText !== '' && $this->captionTextExtends($lastText, $text)) {
                    $compact[$lastCaptionIndex] = $segment;

                    continue;
                }

                if ($lastText !== '' && $this->captionTextExtends($text, $lastText)) {
                    continue;
                }
            }

            $compact[] = $segment;
        }

        return $compact;
    }

    /**
     * @param  array<int, array<string, mixed>>  $segments
     */
    protected function lastCaptionIndex(array $segments): ?int
    {
        for ($index = count($segments) - 1; $index >= 0; $index -= 1) {
            if (($segments[$index]['source'] ?? '') !== 'chat') {
                return $index;
            }
        }

        return null;
    }

    protected function captionTextsEquivalent(string $left, string $right): bool
    {
        return mb_strtolower(trim($left)) === mb_strtolower(trim($right));
    }

    protected function captionTextExtends(string $shorter, string $longer): bool
    {
        $shorter = mb_strtolower(trim($shorter));
        $longer = mb_strtolower(trim($longer));

        if ($shorter === '' || $longer === '') {
            return false;
        }

        return str_starts_with($longer, $shorter);
    }

    /**
     * @param  array<int, array<string, mixed>>  $segments
     */
    protected function segmentsToContent(array $segments): string
    {
        return collect($segments)
            ->map(fn ($segment) => ($segment['speaker'] ?? 'Speaker').': '.($segment['text'] ?? ''))
            ->implode("\n");
    }
}
