<?php

declare(strict_types=1);

namespace ImovelZapAi\Conversations\Application\Services;

use ImovelZapAi\AiGateway\Application\Data\AiCompletionRequest;
use ImovelZapAi\AiGateway\Application\Data\AiMessageData;
use ImovelZapAi\AiGateway\Application\Services\AiGateway;
use ImovelZapAi\AiGateway\Domain\Enums\AiTaskType;
use ImovelZapAi\AiGateway\Infrastructure\Providers\OpenAiProvider;
use ImovelZapAi\Conversations\Application\Data\ConversationSummaryContextData;
use ImovelZapAi\Conversations\Application\Data\ConversationSummaryResultData;
use Throwable;

final class ConversationSummaryAiGenerator
{
    public function __construct(
        private readonly AiGateway $aiGateway,
        private readonly OpenAiProvider $openAiProvider,
        private readonly ConversationSummaryRuleGenerator $fallback,
    ) {}

    public function canGenerate(): bool
    {
        return $this->aiGateway->isEnabled() && $this->openAiProvider->isConfigured();
    }

    public function generate(ConversationSummaryContextData $context): ?ConversationSummaryResultData
    {
        if (! $this->canGenerate()) {
            return null;
        }

        try {
            $response = $this->aiGateway->complete(new AiCompletionRequest(
                tenantId: $context->tenantId,
                taskType: AiTaskType::ConversationSummary,
                messages: [
                    new AiMessageData('system', $this->systemPrompt()),
                    new AiMessageData('user', json_encode($this->payload($context), JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR)),
                ],
                metadata: [
                    'conversation_id' => $context->conversationId,
                ],
                temperature: 0.3,
                maxTokens: 800,
            ));

            $parsed = $this->parseResponse($response->text);

            if ($parsed === null) {
                return null;
            }

            return $this->buildResult($context, $parsed);
        } catch (Throwable) {
            return null;
        }
    }

    private function systemPrompt(): string
    {
        return <<<'PROMPT'
Você é assistente de corretores imobiliários no Brasil. Gere um resumo estruturado em JSON para handoff humano.

Responda APENAS com JSON válido (sem markdown), neste formato:
{
  "client_objective": "frase curta com o objetivo do cliente",
  "bullets": ["bullet 1", "bullet 2"],
  "presented_property_codes": ["AP-103"],
  "favorite_property_codes": ["AP-118"],
  "pending_items": ["item pendente"],
  "recommended_next_step": "ação concreta para o corretor"
}

Regras:
- Português do Brasil, tom profissional e direto.
- Use os códigos de imóvel fornecidos no contexto (formato AP-XXX).
- Não invente imóveis, valores ou bairros ausentes do contexto.
- Bullets com frases completas terminadas em ponto.
- recommended_next_step deve ser uma ação prática e específica.
PROMPT;
    }

    /**
     * @return array<string, mixed>
     */
    private function payload(ConversationSummaryContextData $context): array
    {
        return [
            'client_name' => $context->clientName,
            'search' => [
                'type' => $context->search->type?->value,
                'neighborhood' => $context->search->neighborhood,
                'city' => $context->search->city,
                'max_price' => $context->search->maxPrice,
                'purpose' => $context->search->purpose?->value,
                'bedrooms' => $context->search->minBedrooms,
            ],
            'qualification' => $context->qualification,
            'presented_property_codes' => $context->presentedPropertyCodes,
            'favorite_property_codes' => $context->favoritePropertyCodes,
            'requested_visit' => $context->requestedVisit,
            'conversation_excerpts' => $context->messageExcerpts,
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    private function parseResponse(string $text): ?array
    {
        $text = trim($text);

        if (str_starts_with($text, '```')) {
            $text = preg_replace('/^```(?:json)?\s*/i', '', $text) ?? $text;
            $text = preg_replace('/\s*```$/', '', $text) ?? $text;
        }

        $decoded = json_decode($text, true);

        if (! is_array($decoded) || ! isset($decoded['bullets']) || ! is_array($decoded['bullets'])) {
            return null;
        }

        return $decoded;
    }

    /**
     * @param  array<string, mixed>  $parsed
     */
    private function buildResult(ConversationSummaryContextData $context, array $parsed): ConversationSummaryResultData
    {
        $bullets = $this->stringList($parsed['bullets'] ?? []);
        $presented = $this->mergeCodes(
            $context->presentedPropertyCodes,
            $this->stringList($parsed['presented_property_codes'] ?? []),
        );
        $favorites = $this->mergeCodes(
            $context->favoritePropertyCodes,
            $this->stringList($parsed['favorite_property_codes'] ?? []),
        );
        $pending = $this->stringList($parsed['pending_items'] ?? []);
        $nextStep = trim((string) ($parsed['recommended_next_step'] ?? ''));

        if ($nextStep === '') {
            $fallback = $this->fallback->generate($context);

            $nextStep = $fallback->recommendedNextStep;
        }

        if ($pending === []) {
            $pending = $this->fallback->generate($context)->pendingItems;
        }

        $objective = trim((string) ($parsed['client_objective'] ?? ''));

        if ($objective === '') {
            $objective = $bullets[0] ?? 'Acompanhar interesse do cliente';
        }

        if ($bullets === []) {
            return $this->fallback->generate($context);
        }

        $structured = [
            'summary_version' => 2,
            'client_name' => $context->clientName,
            'client_objective' => $objective,
            'bullets' => $bullets,
            'presented_property_codes' => $presented,
            'favorite_property_codes' => $favorites,
            'liked_property_codes' => $favorites,
            'pending_items' => $pending,
            'recommended_next_step' => $nextStep,
            'search' => $this->fallback->generate($context)->structured['search'] ?? [],
            'qualification' => $context->qualification,
            'flags' => [
                'available_to_talk' => true,
                'requested_visit' => $context->requestedVisit,
            ],
            'generated_by' => 'ai',
            'generated_at' => now()->toIso8601String(),
        ];

        $body = $this->renderBody($bullets, $nextStep);

        return new ConversationSummaryResultData(
            body: $body,
            clientObjective: $objective,
            bullets: $bullets,
            presentedPropertyCodes: $presented,
            favoritePropertyCodes: $favorites,
            pendingItems: $pending,
            recommendedNextStep: $nextStep,
            structured: $structured,
            generatedBy: 'ai',
        );
    }

    /**
     * @param  list<string>  $bullets
     */
    private function renderBody(array $bullets, string $nextStep): string
    {
        $lines = ['Resumo', ''];

        foreach ($bullets as $bullet) {
            $lines[] = '• '.rtrim($bullet, '.').'.';
        }

        $lines[] = '';
        $lines[] = 'Próximo passo recomendado:';
        $lines[] = rtrim($nextStep, '.').'.';

        return implode("\n", $lines);
    }

    /**
     * @return list<string>
     */
    private function stringList(mixed $value): array
    {
        if (! is_array($value)) {
            return [];
        }

        return array_values(array_filter(array_map(
            static fn (mixed $item): string => trim((string) $item),
            $value,
        ), static fn (string $item): bool => $item !== ''));
    }

    /**
     * @param  list<string>  $base
     * @param  list<string>  $fromAi
     * @return list<string>
     */
    private function mergeCodes(array $base, array $fromAi): array
    {
        if ($base === []) {
            return $fromAi;
        }

        return array_values(array_unique([...$base, ...$fromAi]));
    }
}
