<?php

namespace App\Services\Ai;

use App\Models\Central\Company;
use App\Models\Tenant\AiChat;
use App\Models\Tenant\AiMessage;
use App\Models\Tenant\Client;
use App\Models\Tenant\Lead;
use App\Models\Tenant\Part;
use App\Models\Tenant\Vehicle;
use App\Models\Tenant\WorkOrder;
use App\Tenancy\TenantManager;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;

/**
 * Multi-provider AI client (Claude / GPT / Gemini). Provider + API key are
 * read from tenant settings.ai. Chat history persists in ai_chats/ai_messages.
 */
class AiAssistantService
{
    /** Per-provider default model + the dropdown options exposed in Settings. */
    public const MODEL_DEFAULTS = [
        'claude' => 'claude-sonnet-4-6',
        'gpt' => 'gpt-4o-mini',
        'gemini' => 'gemini-1.5-flash',
    ];

    public const MODEL_OPTIONS = [
        'claude' => [
            'claude-opus-4-7' => 'Opus 4.7 — cel mai capabil',
            'claude-sonnet-4-6' => 'Sonnet 4.6 — echilibrat (recomandat)',
            'claude-haiku-4-5-20251001' => 'Haiku 4.5 — rapid și ieftin',
        ],
        'gpt' => [
            'gpt-4o' => 'GPT-4o',
            'gpt-4o-mini' => 'GPT-4o mini (recomandat)',
        ],
        'gemini' => [
            'gemini-1.5-pro' => 'Gemini 1.5 Pro',
            'gemini-1.5-flash' => 'Gemini 1.5 Flash (recomandat)',
        ],
    ];

    /** Resolve the model id for a provider — tenant override > global default. */
    public function modelFor(string $provider, ?Company $company = null): string
    {
        $company ??= $this->currentCompany();
        $override = $company ? data_get($company->settings, "ai.models.{$provider}") : null;
        return $override ?: (self::MODEL_DEFAULTS[$provider] ?? 'claude-sonnet-4-6');
    }

    public function ask(AiChat $chat, string $userMessage): AiMessage
    {
        // Persist user message.
        AiMessage::create([
            'company_id' => $chat->company_id,
            'ai_chat_id' => $chat->id,
            'role' => 'user',
            'content' => $userMessage,
        ]);

        $company = Company::withoutGlobalScopes()->findOrFail($chat->company_id);
        $aiCfg = (array) ($company->settings['ai'] ?? []);
        $provider = $chat->provider ?: ($aiCfg['default_provider'] ?? 'claude');
        $apiKey = $aiCfg["{$provider}_key"] ?? null;

        $start = microtime(true);

        if (! $apiKey) {
            $reply = '⚠️ API key pentru ' . strtoupper($provider) . ' nu e configurat. Mergi la Setări → AI și adaugă cheia.';
            $meta = ['error' => 'no_api_key'];
        } else {
            try {
                [$reply, $meta] = match ($provider) {
                    'claude' => $this->callClaude($apiKey, $chat, $userMessage, $company),
                    'gpt' => $this->callOpenAI($apiKey, $chat, $userMessage, $company),
                    'gemini' => $this->callGemini($apiKey, $chat, $userMessage, $company),
                    default => ['Provider necunoscut: ' . $provider, []],
                };
            } catch (\Throwable $e) {
                $reply = '❌ Eroare API: ' . $e->getMessage();
                $meta = ['error' => $e->getMessage()];
            }
        }

        $meta['latency_ms'] = (int) ((microtime(true) - $start) * 1000);
        $meta['provider'] = $provider;

        $msg = AiMessage::create([
            'company_id' => $chat->company_id,
            'ai_chat_id' => $chat->id,
            'role' => 'assistant',
            'content' => $reply,
            'meta' => $meta,
        ]);

        // Update chat title from first user message.
        if ($chat->messages()->where('role', 'user')->count() === 1) {
            $chat->update(['title' => mb_substr($userMessage, 0, 60)]);
        }
        $chat->touch();

        return $msg;
    }

    protected function buildSystemPrompt(Company $company): string
    {
        $clientsCount = Client::count();
        $vehiclesCount = Vehicle::count();
        $newLeads = Lead::where('status', 'new')->count();
        $openWO = WorkOrder::whereNotIn('status', ['done', 'cancelled'])->count();
        $debtTotal = (float) WorkOrder::where('pay_status', '!=', 'paid')
            ->whereNotIn('status', ['cancelled'])
            ->get()->sum(fn ($w) => max(0, (float) $w->total - (float) $w->payments()->sum('amount')));

        return <<<TXT
Ești asistent CRM pentru autoservice "{$company->name}" din {$company->city}.
Răspunzi în română (sau în limba folosită de utilizator). Fii concis, profesional, util.

Statistici curente:
- {$clientsCount} clienți, {$vehiclesCount} mașini
- {$newLeads} cereri noi neprocesate
- {$openWO} fișe de lucru active
- Total datorii clienți: {$debtTotal} MDL

Poți ajuta cu: sugestii diagnostic auto, calcul costuri, sumar performanță,
recomandări piese, redactare mesaje pentru clienți, planificare. NU inventezi
date care nu apar în context — dacă nu știi, recunoaște.
TXT;
    }

    /** Convert chat history to provider-specific message format. */
    protected function historyMessages(AiChat $chat, int $limit = 20): array
    {
        return $chat->messages()
            ->latest('created_at')
            ->limit($limit)
            ->get()
            ->reverse()
            ->values()
            ->map(fn (AiMessage $m) => ['role' => $m->role, 'content' => $m->content])
            ->all();
    }

    protected function callClaude(string $key, AiChat $chat, string $msg, Company $company): array
    {
        $messages = $this->historyMessages($chat);
        $messages = array_values(array_filter($messages, fn ($m) => in_array($m['role'], ['user', 'assistant'], true)));

        // Normalize history to the structured content-block form (Claude tool-use
        // requires content blocks for the assistant turn that emitted tool_use).
        foreach ($messages as &$m) {
            if (is_string($m['content'])) {
                $m['content'] = [['type' => 'text', 'text' => $m['content']]];
            }
        }
        unset($m);

        $headers = [
            'x-api-key' => $key,
            'anthropic-version' => '2023-06-01',
            'content-type' => 'application/json',
        ];
        $system = $this->buildSystemPrompt($company);
        $tools = AiToolExecutor::TOOLS;
        $executor = app(AiToolExecutor::class);

        $tokensIn = 0;
        $tokensOut = 0;
        $toolCalls = [];
        $finalText = '';
        $model = null;

        // Loop on tool_use up to 5 rounds, then bail out with whatever text we have.
        for ($round = 0; $round < 5; $round++) {
            $r = Http::withHeaders($headers)->timeout(60)->post(
                'https://api.anthropic.com/v1/messages',
                [
                    'model' => $this->modelFor('claude', $company),
                    'max_tokens' => 1024,
                    'system' => $system,
                    'tools' => $tools,
                    'messages' => $messages,
                ]
            );

            if (! $r->successful()) {
                return ['❌ ' . ($r->json('error.message') ?? 'Anthropic API error ' . $r->status()), ['status' => $r->status()]];
            }

            $body = $r->json();
            $model = $body['model'] ?? $model;
            $tokensIn += (int) ($body['usage']['input_tokens'] ?? 0);
            $tokensOut += (int) ($body['usage']['output_tokens'] ?? 0);

            $blocks = $body['content'] ?? [];
            $finalText = collect($blocks)->where('type', 'text')->pluck('text')->implode("\n");

            if (($body['stop_reason'] ?? null) !== 'tool_use') {
                break;
            }

            // Append the assistant turn (with the tool_use block) to history.
            $messages[] = ['role' => 'assistant', 'content' => $blocks];

            // Execute every tool_use block and build the user reply with tool_results.
            $toolResults = [];
            foreach ($blocks as $b) {
                if (($b['type'] ?? '') !== 'tool_use') continue;
                $name = $b['name'];
                $input = (array) ($b['input'] ?? []);
                try {
                    $out = $executor->execute($name, $input);
                } catch (\Throwable $e) {
                    $out = ['error' => $e->getMessage()];
                }
                $toolCalls[] = ['name' => $name, 'input' => $input];
                $toolResults[] = [
                    'type' => 'tool_result',
                    'tool_use_id' => $b['id'],
                    'content' => json_encode($out, JSON_UNESCAPED_UNICODE),
                ];
            }
            $messages[] = ['role' => 'user', 'content' => $toolResults];
        }

        return [$finalText ?: '(răspuns gol)', [
            'model' => $model,
            'tokens_in' => $tokensIn,
            'tokens_out' => $tokensOut,
            'tools' => $toolCalls,
        ]];
    }

    protected function callOpenAI(string $key, AiChat $chat, string $msg, Company $company): array
    {
        $messages = array_merge(
            [['role' => 'system', 'content' => $this->buildSystemPrompt($company)]],
            $this->historyMessages($chat),
        );

        $r = Http::withHeaders(['Authorization' => 'Bearer ' . $key, 'content-type' => 'application/json'])
            ->timeout(60)
            ->post('https://api.openai.com/v1/chat/completions', [
                'model' => $this->modelFor('gpt', $company),
                'messages' => $messages,
                'max_tokens' => 1024,
            ]);

        if (! $r->successful()) {
            return ['❌ ' . ($r->json('error.message') ?? 'OpenAI error ' . $r->status()), ['status' => $r->status()]];
        }
        $body = $r->json();
        return [
            $body['choices'][0]['message']['content'] ?? '(răspuns gol)',
            [
                'model' => $body['model'] ?? null,
                'tokens_in' => $body['usage']['prompt_tokens'] ?? null,
                'tokens_out' => $body['usage']['completion_tokens'] ?? null,
            ],
        ];
    }

    // ─── One-shot AI helpers ─────────────────────────────────────────

    /**
     * Single-prompt call without persisting a chat. Used by quick-action buttons
     * (diagnose / suggest parts / suggest price / VIN recommendations).
     *
     * @return array{0:string, 1:array} [reply, meta]
     */
    public function singleShot(string $systemPrompt, string $userPrompt, ?string $provider = null): array
    {
        $company = $this->currentCompany();
        if (! $company) return ['Niciun tenant rezolvat.', ['error' => 'no_tenant']];

        $aiCfg = (array) ($company->settings['ai'] ?? []);
        $provider ??= $aiCfg['default_provider'] ?? 'claude';
        $key = $aiCfg["{$provider}_key"] ?? null;
        if (! $key) {
            return ['⚠️ API key pentru ' . strtoupper($provider) . ' lipsește în Setări → AI.',
                    ['error' => 'no_api_key', 'provider' => $provider]];
        }

        $start = microtime(true);
        try {
            [$reply, $meta] = match ($provider) {
                'claude' => $this->postClaude($key, $systemPrompt, [['role' => 'user', 'content' => $userPrompt]]),
                'gpt' => $this->postOpenAI($key, $systemPrompt, [['role' => 'user', 'content' => $userPrompt]]),
                'gemini' => $this->postGemini($key, $systemPrompt, [['role' => 'user', 'content' => $userPrompt]]),
                default => ['Provider necunoscut: ' . $provider, []],
            };
        } catch (\Throwable $e) {
            $reply = '❌ Eroare API: ' . $e->getMessage();
            $meta = ['error' => $e->getMessage()];
        }
        $meta['latency_ms'] = (int) ((microtime(true) - $start) * 1000);
        $meta['provider'] = $provider;

        return [$reply, $meta];
    }

    public function suggestDiagnosis(WorkOrder $wo): array
    {
        $vehicle = $wo->vehicle;
        $vinInfo = '';
        if ($vehicle?->vin) {
            $d = app(VinDecoder::class)->decode($vehicle->vin);
            if (! empty($d['manufacturer'])) {
                $vinInfo = " (VIN decode: {$d['manufacturer']} {$d['year']})";
            }
        }
        $sys = "Ești diagnostician auto cu 20 de ani experiență. Răspunzi scurt, structurat, "
             . "în română. Listează în ordine de probabilitate cauzele posibile + verificările "
             . "necesare. Nu inventezi date — dacă plângerea e vagă, sugerezi întrebări de clarificat.";
        $user = sprintf(
            "Mașina: %s %s %s%s, %s km.\nPlângere client: %s\n\nDă-mi top 3 cauze probabile și ce trebuie verificat la fiecare.",
            (string) ($vehicle->make ?? '?'),
            (string) ($vehicle->model ?? '?'),
            (string) ($vehicle->year ?? ''),
            $vinInfo,
            number_format((float) ($vehicle->mileage ?? 0), 0, '.', ' '),
            $wo->complaint ?: '(nu e completată)',
        );
        return $this->singleShot($sys, $user);
    }

    public function suggestParts(WorkOrder $wo, string $task): array
    {
        $vehicle = $wo->vehicle;
        $sys = "Ești expert piese auto. Pentru mașina dată și operațiunea solicitată, "
             . "listează piesele necesare cu coduri OEM tipice când le știi (sau familie generică). "
             . "Răspunde în română, format listă.";
        $user = sprintf(
            "Mașina: %s %s %s.\nOperațiune: %s.\nListează piesele necesare + coduri OEM dacă există + cantități.",
            (string) ($vehicle->make ?? '?'),
            (string) ($vehicle->model ?? '?'),
            (string) ($vehicle->year ?? ''),
            $task,
        );
        return $this->singleShot($sys, $user);
    }

    public function suggestPrice(Part $part): array
    {
        $company = $this->currentCompany();
        $currency = (string) data_get($company?->settings, 'currency', 'MDL');
        $sys = "Ești manager comercial pentru un magazin de piese auto din Moldova. "
             . "Sugerezi preț de vânzare bazat pe costul de achiziție, categorie și brand. "
             . "Răspunde scurt cu: preț recomandat, markup %, justificare 1-2 fraze.";
        $user = sprintf(
            "Piesa: %s\nBrand: %s\nCategorie: %s\nCost achiziție: %.2f %s\nPreț actual: %.2f %s",
            $part->name,
            $part->brand ?? '?',
            $part->category ?? '?',
            (float) $part->buy_price, $currency,
            (float) $part->sell_price, $currency,
        );
        return $this->singleShot($sys, $user);
    }

    public function vinRecommendations(string $vin, ?int $mileage = null): array
    {
        $decoded = app(VinDecoder::class)->decode($vin);
        if (empty($decoded['manufacturer'])) {
            return ["VIN nu poate fi decodat — verifică formatul (17 caractere).", ['error' => 'invalid_vin']];
        }

        $sys = "Ești expert service auto. Pe baza datelor mașinii și kilometrajului, "
             . "sugerezi mentenanța programată recomandată de producător + verificările "
             . "tipice pentru vârsta mașinii. Format listă scurtă.";
        $user = sprintf(
            "Mașina decodată din VIN: %s din %d (%s).\n%sCe verificări și mentenanță programată recomandăm acum?",
            $decoded['manufacturer'],
            $decoded['year'],
            $decoded['country'] ?? '?',
            $mileage ? "Kilometraj actual: " . number_format($mileage, 0, '.', ' ') . " km.\n" : '',
        );
        [$reply, $meta] = $this->singleShot($sys, $user);
        $meta['vin_decoded'] = $decoded;
        return [$reply, $meta];
    }

    // ─── Token usage tracking ────────────────────────────────────────

    /**
     * Aggregate token spend for current month, grouped by provider.
     *
     * @return array<string, array{tokens_in:int, tokens_out:int, calls:int}>
     */
    public function monthlyUsage(): array
    {
        $start = Carbon::now()->startOfMonth();
        $rows = AiMessage::where('role', 'assistant')
            ->where('created_at', '>=', $start)
            ->get(['meta']);

        $out = [];
        foreach ($rows as $r) {
            $meta = (array) $r->meta;
            $provider = (string) ($meta['provider'] ?? 'unknown');
            $out[$provider] ??= ['tokens_in' => 0, 'tokens_out' => 0, 'calls' => 0];
            $out[$provider]['tokens_in'] += (int) ($meta['tokens_in'] ?? 0);
            $out[$provider]['tokens_out'] += (int) ($meta['tokens_out'] ?? 0);
            $out[$provider]['calls'] += 1;
        }
        return $out;
    }

    // ─── Provider HTTP — common form, used by both chat + single-shot ─

    protected function postClaude(string $key, string $system, array $messages): array
    {
        $r = Http::withHeaders([
                'x-api-key' => $key,
                'anthropic-version' => '2023-06-01',
                'content-type' => 'application/json',
            ])
            ->timeout(60)
            ->post('https://api.anthropic.com/v1/messages', [
                'model' => $this->modelFor('claude'),
                'max_tokens' => 1024,
                'system' => $system,
                'messages' => $messages,
            ]);
        if (! $r->successful()) {
            return ['❌ ' . ($r->json('error.message') ?? 'Anthropic ' . $r->status()), ['status' => $r->status()]];
        }
        $body = $r->json();
        $text = collect($body['content'] ?? [])
            ->where('type', 'text')->pluck('text')->implode("\n");
        return [$text ?: '(răspuns gol)', [
            'model' => $body['model'] ?? null,
            'tokens_in' => $body['usage']['input_tokens'] ?? null,
            'tokens_out' => $body['usage']['output_tokens'] ?? null,
        ]];
    }

    protected function postOpenAI(string $key, string $system, array $messages): array
    {
        $r = Http::withHeaders(['Authorization' => 'Bearer ' . $key, 'content-type' => 'application/json'])
            ->timeout(60)
            ->post('https://api.openai.com/v1/chat/completions', [
                'model' => $this->modelFor('gpt'),
                'messages' => array_merge([['role' => 'system', 'content' => $system]], $messages),
                'max_tokens' => 1024,
            ]);
        if (! $r->successful()) {
            return ['❌ ' . ($r->json('error.message') ?? 'OpenAI ' . $r->status()), ['status' => $r->status()]];
        }
        $body = $r->json();
        return [
            $body['choices'][0]['message']['content'] ?? '(răspuns gol)',
            [
                'model' => $body['model'] ?? null,
                'tokens_in' => $body['usage']['prompt_tokens'] ?? null,
                'tokens_out' => $body['usage']['completion_tokens'] ?? null,
            ],
        ];
    }

    protected function postGemini(string $key, string $system, array $messages): array
    {
        $contents = [];
        foreach ($messages as $m) {
            $contents[] = [
                'role' => $m['role'] === 'assistant' ? 'model' : 'user',
                'parts' => [['text' => $m['content']]],
            ];
        }
        $r = Http::withHeaders(['content-type' => 'application/json'])
            ->timeout(60)
            ->post('https://generativelanguage.googleapis.com/v1beta/models/' . $this->modelFor('gemini') . ':generateContent?key=' . $key, [
                'systemInstruction' => ['parts' => [['text' => $system]]],
                'contents' => $contents,
                'generationConfig' => ['maxOutputTokens' => 1024],
            ]);
        if (! $r->successful()) {
            return ['❌ Gemini ' . $r->status(), ['status' => $r->status()]];
        }
        $body = $r->json();
        $text = $body['candidates'][0]['content']['parts'][0]['text'] ?? '(răspuns gol)';
        return [$text, ['model' => $this->modelFor('gemini'), 'tokens' => $body['usageMetadata'] ?? null]];
    }

    protected function currentCompany(): ?Company
    {
        $id = app(TenantManager::class)->currentId();
        if (! $id) return null;
        return Company::withoutGlobalScopes()->find($id);
    }

    protected function callGemini(string $key, AiChat $chat, string $msg, Company $company): array
    {
        $contents = [];
        foreach ($this->historyMessages($chat) as $m) {
            $contents[] = [
                'role' => $m['role'] === 'assistant' ? 'model' : 'user',
                'parts' => [['text' => $m['content']]],
            ];
        }

        $r = Http::withHeaders(['content-type' => 'application/json'])
            ->timeout(60)
            ->post('https://generativelanguage.googleapis.com/v1beta/models/' . $this->modelFor('gemini', $company) . ':generateContent?key=' . $key, [
                'systemInstruction' => ['parts' => [['text' => $this->buildSystemPrompt($company)]]],
                'contents' => $contents,
                'generationConfig' => ['maxOutputTokens' => 1024],
            ]);

        if (! $r->successful()) {
            return ['❌ Gemini error ' . $r->status() . ': ' . $r->body(), ['status' => $r->status()]];
        }
        $body = $r->json();
        $text = $body['candidates'][0]['content']['parts'][0]['text'] ?? '(răspuns gol)';
        return [$text, [
            'model' => $this->modelFor('gemini', $company),
            'tokens' => $body['usageMetadata'] ?? null,
        ]];
    }
}
