<?php

namespace App\Http\Controllers;

use App\Agent\Choices;
use App\Agent\GoogleAdsAgent;
use App\Agent\Workspace;
use App\Models\Creative;
use App\Models\GoogleAdsCampaign;
use App\Models\GoogleAdsConnection;
use App\Services\GoogleAdsService;
use App\Support\Markdown;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\View\View;
use Laravel\Ai\Models\Conversation;
use Throwable;

class GoogleChatController extends Controller
{
    public function __construct(private readonly Workspace $workspace) {}

    public function show(Request $request): View
    {
        $conversation = $this->resolveConversation($request);

        if ($conversation) {
            $this->workspace->bindTo($conversation->id);
        }

        return view('googlechat', [
            'pageTitle' => 'Google Ads chat',
            'conversationId' => $conversation?->id,
            'transcript' => $this->transcript($conversation),
            'awaiting' => [],
            'choices' => $conversation ? $this->workspace->pendingChoices() : [],
            'conversations' => $this->recentConversations($request),
        ]);
    }

    public function create(): RedirectResponse
    {
        return to_route('googlechat.show', ['new' => 1]);
    }

    public function send(Request $request): JsonResponse
    {
        $startedAt = now();
        $validated = $request->validate([
            'message' => ['required', 'string', 'max:4000'],
            'conversation' => ['nullable', 'string', 'max:36'],
            'platform' => ['nullable', 'in:google,both'],
            'landing_url' => ['nullable', 'url:http,https', 'max:2048'],
            'answers' => ['nullable', 'array'],
        ]);
        $message = trim($validated['message']);
        foreach ($validated['answers'] ?? [] as $answer) {
            if (is_array($answer) && filled($answer['field'] ?? null) && filled($answer['value'] ?? null)) {
                $value = is_array($answer['value']) ? implode(', ', $answer['value']) : $answer['value'];
                $message .= ' Set '.$answer['field'].' to '.$value.'.';
            }
        }
        if (! empty($validated['landing_url'])) {
            $message .= ' Landing page URL: '.$validated['landing_url'];
        }
        if (! empty($validated['platform'])) {
            $message .= ' Selected platform: '.$validated['platform'].'.';
        }

        $agent = $validated['conversation'] ?? null
            ? $this->agent($request)->continue(
                $this->ownedConversation($request, $validated['conversation'])->id,
                $request->user(),
            )
            : $this->agent($request)->forParticipant($request->user());

        if ($validated['conversation'] ?? null) {
            $this->workspace->bindTo($validated['conversation']);
            $this->workspace->rememberChoices([]);
        }

        try {
            $response = $agent->prompt($message);
            $reply = trim((string) $response->text);
            $choices = app(Choices::class)->all();
            app(Choices::class)->drain();
            if ($choices === [] && preg_match('/select image creative|no completed.*(?:landscape|square|image)|landscape image|square image/i', $reply)) {
                $this->offerImageChoices($request, $reply);
                $choices = app(Choices::class)->all();
                app(Choices::class)->drain();
            }
            $this->workspace->bindTo($agent->currentConversation());
            $this->workspace->rememberChoices($choices);
            $conversation = $this->rememberConversation($agent->currentConversation());
            $this->attachCampaigns($request, $conversation, $startedAt);
        } catch (Throwable $exception) {
            report($exception);
            $reply = 'That Google Ads step failed: '.$exception->getMessage();
            $this->recordFailedTurn($agent->currentConversation(), $request, $message, $reply);
        }

        $reply = $reply !== '' ? $reply : 'Google Ads did not return a text response.';

        return response()->json([
            'conversation' => $agent->currentConversation(),
            'conversation_title' => $this->conversationTitle($agent->currentConversation()),
            'reply' => $reply,
            'replyHtml' => (string) Markdown::toHtml($reply),
            'awaiting_approval' => [],
            'choices' => $choices ?? [],
        ]);
    }

    private function offerImageChoices(Request $request, string $reply): void
    {
        $field = str_contains(strtolower($reply), 'square')
            ? 'square_marketing_image_assets'
            : 'marketing_image_assets';
        $required = $field === 'square_marketing_image_assets' ? [1200, 1200] : [1200, 628];
        $images = Creative::query()
            ->where('user_id', $request->user()->getKey())
            ->where('type', 'image')
            ->where('status', 'completed')
            ->whereNotNull('asset_id')
            ->with('asset')
            ->latest('id')
            ->limit(60)
            ->get()
            ->filter(function (Creative $creative) use ($required): bool {
                $asset = $creative->asset;
                if ($asset === null || $asset->kind !== 'image') {
                    return false;
                }
                $path = Storage::disk($asset->disk ?: 'local')->path($asset->path);
                $size = @getimagesize($path);

                return is_array($size) && [$size[0] ?? null, $size[1] ?? null] === $required;
            });
        if ($images->isEmpty()) {
            return;
        }

        app(Choices::class)->offer(
            field: $field,
            question: 'Select an image creative to upload to Google Ads.',
            options: $images->map(fn (Creative $creative): array => [
                'value' => (string) $creative->id,
                'label' => $creative->title ?: 'Creative '.$creative->id,
                'hint' => $required[0].'x'.$required[1].' - '.$creative->asset->original_name,
                'image' => route('assets.preview', $creative->asset),
                'sends' => 'Select image creative '.$creative->id,
            ])->all(),
        );
    }

    public function uploadImage(Request $request, GoogleAdsService $googleAds): JsonResponse
    {
        $data = $request->validate([
            'conversation' => ['required', 'string', 'max:36'],
            'image_field' => ['required', 'in:marketing_image_assets,square_marketing_image_assets,logo_image_assets'],
            'image' => ['required', 'file', 'image', 'max:10240'],
        ]);
        $conversation = $this->ownedConversation($request, $data['conversation']);
        $connection = GoogleAdsConnection::query()->where('user_id', $request->user()->getKey())->first();
        if ($connection === null || blank($connection->customer_id) || blank($connection->refresh_token)) {
            return response()->json(['message' => 'Select a Google Ads child account before uploading an image.'], 422);
        }

        /** @var UploadedFile $image */
        $image = $data['image'];
        $dimensions = @getimagesize($image->getRealPath());
        $required = in_array($data['image_field'], ['square_marketing_image_assets', 'logo_image_assets'], true) ? [1200, 1200] : [1200, 628];
        if (! is_array($dimensions) || [$dimensions[0] ?? null, $dimensions[1] ?? null] !== $required) {
            return response()->json(['message' => 'Upload an image with exact dimensions '.$required[0].'x'.$required[1].'.'], 422);
        }

        try {
            $resourceName = $googleAds->createImageAsset($connection, (string) file_get_contents($image->getRealPath()), $image->getClientOriginalName());
            $draft = json_decode((string) $conversation->google_campaign_draft, true);
            $draft = is_array($draft) ? $draft : [];
            $draft[$data['image_field']] = $resourceName;
            $conversation->update(['google_campaign_draft' => json_encode($draft)]);
        } catch (Throwable $exception) {
            report($exception);

            return response()->json(['message' => $exception->getMessage()], 502);
        }

        return response()->json([
            'ok' => true,
            'field' => $data['image_field'],
            'asset_resource_name' => $resourceName,
            'dimensions' => ['width' => $dimensions[0], 'height' => $dimensions[1]],
            'message' => 'Image uploaded to Google Ads and saved to the campaign draft.',
        ]);
    }

    private function agent(Request $request): GoogleAdsAgent
    {
        return new GoogleAdsAgent($request, $this->workspace);
    }

    private function resolveConversation(Request $request): ?Conversation
    {
        if ($request->boolean('new')) {
            return null;
        }

        $id = $request->string('conversation')->toString();

        return $id !== '' ? $this->ownedConversation($request, $id) : null;
    }

    private function ownedConversation(Request $request, string $id): Conversation
    {
        $conversation = Conversation::find($id);

        abort_if(
            $conversation === null
            || $conversation->chat_type !== 'google_ads'
            || $conversation->participant_type !== $request->user()?->getMorphClass()
            || (string) $conversation->participant_id !== (string) $request->user()?->getKey(),
            404,
        );

        return $conversation;
    }

    private function recentConversations(Request $request)
    {
        return Conversation::query()
            ->where('chat_type', 'google_ads')
            ->where('participant_type', $request->user()?->getMorphClass())
            ->where('participant_id', $request->user()?->getKey())
            ->latest('updated_at')
            ->limit(30)
            ->get();
    }

    private function conversationTitle(?string $id): string
    {
        return trim((string) ($id ? Conversation::find($id)?->title : null)) ?: 'Google Ads chat';
    }

    private function transcript(?Conversation $conversation): array
    {
        if (! $conversation) {
            return [];
        }

        return DB::table('agent_conversation_messages')
            ->where('conversation_id', $conversation->id)
            ->whereIn('role', ['user', 'assistant'])
            ->orderBy('created_at')
            ->orderBy('id')
            ->get(['role', 'content'])
            ->filter(fn ($message): bool => trim((string) $message->content) !== '')
            ->map(fn ($message): array => [
                'role' => $message->role === 'user' ? 'user' : 'agent',
                'text' => (string) $message->content,
                'html' => $message->role === 'user' ? null : (string) Markdown::toHtml((string) $message->content),
            ])
            ->values()
            ->all();
    }

    private function rememberConversation(?string $conversationId): ?Conversation
    {
        if (blank($conversationId)) {
            return null;
        }

        $conversation = Conversation::find($conversationId);

        if (! $conversation) {
            return null;
        }

        $conversation->update(['chat_type' => 'google_ads']);

        $conversation->touch();

        return $conversation;
    }

    private function attachCampaigns(Request $request, ?Conversation $conversation, $startedAt): void
    {
        if (! $conversation) {
            return;
        }

        GoogleAdsCampaign::query()
            ->where('user_id', $request->user()->getKey())
            ->whereNull('google_chat_conversation_id')
            ->where('created_at', '>=', $startedAt)
            ->update(['google_chat_conversation_id' => $conversation->id]);
    }

    private function recordFailedTurn(?string $conversationId, Request $request, string $message, string $reply): void
    {
        if (blank($conversationId)) {
            return;
        }

        $base = [
            'conversation_id' => $conversationId,
            'participant_type' => $request->user()?->getMorphClass(),
            'participant_id' => $request->user()?->getKey(),
            'agent' => GoogleAdsAgent::class,
            'attachments' => '[]',
            'tool_calls' => '[]',
            'tool_results' => '[]',
            'usage' => '[]',
            'meta' => '[]',
            'created_at' => now(),
            'updated_at' => now(),
        ];

        DB::table('agent_conversation_messages')->insert([
            [...$base, 'id' => (string) Str::uuid7(), 'role' => 'user', 'content' => $message],
            [...$base, 'id' => (string) Str::uuid7(), 'role' => 'assistant', 'content' => $reply],
        ]);
    }
}
