<?php

namespace App\Services;

use App\Models\BiataBotConversation;
use App\Models\BiataBotMessage;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Str;

class BiataBotChatStore
{
    public const GUEST_COOKIE = 'biata_guest';

    public function guestKey(bool $create = true): ?string
    {
        $key = Cookie::get(self::GUEST_COOKIE);
        if (is_string($key) && preg_match('/^[a-f0-9-]{36}$/i', $key)) {
            return $key;
        }

        if (!$create) {
            return null;
        }

        $key = (string) Str::uuid();
        Cookie::queue(cookie(self::GUEST_COOKIE, $key, 60 * 24 * 365, '/', null, false, true, false, 'Lax'));

        return $key;
    }

    public function findExistingConversation(?User $user = null): ?BiataBotConversation
    {
        $user = $user ?? Auth::user();

        if ($user) {
            return BiataBotConversation::where('user_id', $user->id)->first();
        }

        $guestKey = $this->guestKey(false);
        if (!$guestKey) {
            return null;
        }

        return BiataBotConversation::whereNull('user_id')
            ->where('guest_key', $guestKey)
            ->first();
    }

    public function resolveConversation(?User $user = null): BiataBotConversation
    {
        $user = $user ?? Auth::user();
        $guestKey = $this->guestKey(true);

        if ($user) {
            $conversation = BiataBotConversation::firstOrCreate(
                ['user_id' => $user->id],
                ['guest_key' => $guestKey, 'messages_count' => 0]
            );

            // Merge orphan guest thread into this user account (once).
            $guestConversation = BiataBotConversation::whereNull('user_id')
                ->where('guest_key', $guestKey)
                ->where('id', '!=', $conversation->id)
                ->first();

            if ($guestConversation) {
                BiataBotMessage::where('conversation_id', $guestConversation->id)
                    ->update(['conversation_id' => $conversation->id]);
                $conversation->messages_count = $conversation->messages()->count();
                $conversation->last_message_at = $conversation->messages()->latest('id')->value('created_at');
                $conversation->guest_key = $guestKey;
                $conversation->save();
                $guestConversation->delete();
            }

            return $conversation;
        }

        $conversation = BiataBotConversation::whereNull('user_id')
            ->where('guest_key', $guestKey)
            ->first();

        if ($conversation) {
            return $conversation;
        }

        return BiataBotConversation::create([
            'user_id'        => null,
            'guest_key'      => $guestKey,
            'messages_count' => 0,
        ]);
    }

    public function append(BiataBotConversation $conversation, string $role, string $content): BiataBotMessage
    {
        $message = $conversation->messages()->create([
            'role'    => $role === 'assistant' ? 'assistant' : 'user',
            'content' => $content,
        ]);

        $conversation->messages_count = ($conversation->messages_count ?? 0) + 1;
        $conversation->last_message_at = now();
        $conversation->save();

        return $message;
    }

    /**
     * @return list<array{role:string, content:string}>
     */
    public function historyForPrompt(BiataBotConversation $conversation, int $limit = 12): array
    {
        return $conversation->messages()
            ->orderByDesc('id')
            ->limit($limit)
            ->get()
            ->sortBy('id')
            ->values()
            ->map(fn (BiataBotMessage $m) => [
                'role'    => $m->role,
                'content' => $m->content,
            ])
            ->all();
    }

    /**
     * @return list<array{role:string, content:string, at:string}>
     */
    public function historyForClient(BiataBotConversation $conversation, int $limit = 100): array
    {
        return $conversation->messages()
            ->orderBy('id')
            ->limit($limit)
            ->get()
            ->map(fn (BiataBotMessage $m) => [
                'role'    => $m->role,
                'content' => $m->content,
                'at'      => $m->created_at?->toIso8601String(),
            ])
            ->all();
    }
}
