<?php

namespace App\Services;

use App\Enums\Currency;
use App\Enums\PaymentStatus;
use App\Enums\SaleOrigin;
use App\Enums\SaleType;
use App\Models\Account;
use App\Models\Client;
use App\Models\Payment;
use App\Models\Sale;
use App\Models\SystemSetting;
use Carbon\Carbon;
use Illuminate\Support\Facades\Http;

class NunaAssistantService
{
    private const API_URL = 'https://api.anthropic.com/v1/messages';

    // ── Interpretar comando con Claude ────────────────────────────────────────

    /**
     * @param string $command     Último mensaje del usuario
     * @param array  $history     Historial previo [{role, content}, ...]
     */
    public function interpret(string $command, array $history = []): array
    {
        $settings = SystemSetting::current();
        $apiKey   = $settings->claude_api_key;

        if (! $apiKey) {
            return ['type' => 'error', 'message' => 'API Key de Claude no configurada. Ve a Configuración → Configuración global.'];
        }

        // Construir mensajes con historial
        $messages = [];
        foreach ($history as $msg) {
            $messages[] = ['role' => $msg['role'], 'content' => $msg['content']];
        }
        $messages[] = ['role' => 'user', 'content' => $command];

        $response = Http::withHeaders([
            'x-api-key'         => $apiKey,
            'anthropic-version' => '2023-06-01',
            'content-type'      => 'application/json',
        ])->timeout(30)->post(self::API_URL, [
            'model'       => $settings->claude_model ?? 'claude-haiku-4-5-20251001',
            'max_tokens'  => 1024,
            'system'      => $this->buildSystemPrompt(),
            'tools'       => $this->getTools(),
            'tool_choice' => ['type' => 'auto'], // permite responder con texto cuando necesita aclarar
            'messages'    => $messages,
        ]);

        if (! $response->successful()) {
            return ['type' => 'error', 'message' => 'Error al conectar con Claude (HTTP '.$response->status().'). Verificá la API key.'];
        }

        $data = $response->json();

        // Priorizar tool_use sobre texto
        $toolBlock = null;
        $textBlock = null;
        foreach ($data['content'] ?? [] as $block) {
            if ($block['type'] === 'tool_use' && ! $toolBlock) {
                $toolBlock = $block;
            }
            if ($block['type'] === 'text' && ! $textBlock) {
                $textBlock = $block;
            }
        }

        if ($toolBlock) {
            return $this->buildIntent($toolBlock['name'], $toolBlock['input']);
        }
        if ($textBlock) {
            return ['type' => 'text', 'message' => $textBlock['text']];
        }

        return ['type' => 'error', 'message' => 'No se pudo interpretar el comando.'];
    }

    // ── Ejecutar intent confirmado ────────────────────────────────────────────

    public function execute(string $tool, array $input): array
    {
        return match ($tool) {
            'create_sale'      => $this->executeSale($input),
            'register_payment' => $this->executePayment($input),
            'set_follow_up'    => $this->executeFollowUp($input),
            default            => ['type' => 'error', 'message' => "Acción desconocida: $tool"],
        };
    }

    // ── Acciones ─────────────────────────────────────────────────────────────

    private function executeSale(array $input): array
    {
        $client = $this->resolveOrCreateClient(
            $input['client_name'],
            $input['is_new_client'] ?? false,
            $input['client_currency'] ?? 'USD',
            $input['client_origin'] ?? 'manual_local'
        );

        if (! $client) {
            return ['type' => 'error', 'message' => 'No se pudo crear o encontrar el cliente.'];
        }

        $saleType = ($input['sale_type'] ?? 'fixed') === 'hourly'
            ? SaleType::Hourly
            : SaleType::Fixed;

        $grossAmount = match ($saleType) {
            SaleType::Hourly => round(((float)($input['hourly_rate'] ?? 0)) * ((float)($input['hours'] ?? 0)), 2),
            default          => (float)($input['amount'] ?? 0),
        };

        $sale = Sale::create([
            'client_id'               => $client->id,
            'owner_user_id'           => auth()->id(),
            'created_by_user_id'      => auth()->id(),
            'origin'                  => SaleOrigin::Manual,
            'sale_type'               => $saleType,
            'title'                   => $input['title'],
            'currency'                => Currency::from($input['currency'] ?? $client->currency_default?->value ?? 'USD'),
            'fixed_amount'            => $saleType === SaleType::Fixed ? $grossAmount : null,
            'hourly_rate'             => $saleType === SaleType::Hourly ? ($input['hourly_rate'] ?? 0) : null,
            'hours_quantity'          => $saleType === SaleType::Hourly ? ($input['hours'] ?? 0) : null,
            'gross_amount'            => $grossAmount,
            'platform_fee'            => 0,
            'collected_amount_cached' => 0,
            'payment_status'          => PaymentStatus::Unpaid,
            'sale_date'               => now()->toDateString(),
            'expected_collect_date'   => isset($input['expected_collect_date'])
                ? $this->parseDate($input['expected_collect_date'])
                : null,
        ]);

        $symbol = Currency::from($sale->currency->value)->symbol();
        $label  = $saleType === SaleType::Hourly
            ? "{$input['hours']} h × {$symbol}{$input['hourly_rate']} = {$symbol}{$grossAmount}"
            : "{$symbol}{$grossAmount}";

        return [
            'type'    => 'success',
            'message' => "✅ Venta creada: **{$sale->title}** para **{$client->name}** — {$label}",
            'sale_id' => $sale->id,
        ];
    }

    private function executePayment(array $input): array
    {
        $sale = $this->findSaleForPayment(
            $input['client_name'],
            $input['sale_title'] ?? null,
            $input['sale_id'] ?? null
        );

        if (! $sale) {
            return ['type' => 'error', 'message' => "No se encontró una venta pendiente para **{$input['client_name']}**. Verificá el nombre del cliente."];
        }

        $currency = Currency::from($input['currency'] ?? $sale->currency?->value ?? 'USD');
        $amount   = (float) ($input['amount'] ?? 0);
        $account  = $this->resolveAccount($input['account_name'] ?? null, $currency);

        if (! $account) {
            return ['type' => 'error', 'message' => "No se encontró una cuenta de cobro disponible."];
        }

        $paymentDate = $this->parseDate($input['payment_date'] ?? 'hoy');

        $payment = Payment::create([
            'sale_id'            => $sale->id,
            'account_id'         => $account->id,
            'created_by_user_id' => auth()->id(),
            'payment_date'       => $paymentDate,
            'amount'             => $amount,
            'currency'           => $currency,
            'fee_amount'         => 0,
            'net_amount'         => $amount,
            'note'               => $input['note'] ?? 'Registrado vía asistente Nuna',
        ]);

        $symbol = $currency->symbol();

        return [
            'type'       => 'success',
            'message'    => "💰 Pago registrado: **{$symbol}{$amount}** de **{$sale->client?->name}** en {$account->name}",
            'payment_id' => $payment->id,
        ];
    }

    private function executeFollowUp(array $input): array
    {
        $sale = $this->findPendingSaleForClient($input['client_name'], $input['sale_title'] ?? null);

        if (! $sale) {
            return ['type' => 'error', 'message' => "No se encontró una venta pendiente para **{$input['client_name']}**."];
        }

        $date = $this->parseDate($input['date']);
        $type = $input['type'] ?? 'collect';

        if ($type === 'followup') {
            $sale->update([
                'last_follow_up_date' => $date,
                'follow_up_note'      => $input['note'] ?? null,
            ]);
            return ['type' => 'success', 'message' => "📌 Seguimiento programado para {$sale->client?->name} el {$date}"];
        }

        $sale->update(['expected_collect_date' => $date]);
        return ['type' => 'success', 'message' => "📅 Fecha de cobro actualizada para **{$sale->client?->name}**: {$date}"];
    }

    // ── Helpers de resolución ─────────────────────────────────────────────────

    private function resolveOrCreateClient(string $name, bool $isNew, string $currency, string $origin): ?Client
    {
        if (! $isNew) {
            $client = Client::whereRaw('LOWER(name) LIKE ?', ['%'.strtolower($name).'%'])->first();
            if ($client) return $client;
        }

        return Client::create([
            'name'             => $name,
            'currency_default' => Currency::from($currency),
            'origin'           => \App\Enums\ClientOrigin::from($origin),
        ]);
    }

    private function findSaleForPayment(string $clientName, ?string $saleTitle, ?int $saleId): ?Sale
    {
        if ($saleId) {
            return Sale::find($saleId);
        }

        $client = Client::whereRaw('LOWER(name) LIKE ?', ['%'.strtolower($clientName).'%'])->first();
        if (! $client) return null;

        $q = Sale::where('client_id', $client->id)
            ->whereIn('payment_status', [PaymentStatus::Unpaid->value, PaymentStatus::Partial->value]);

        if ($saleTitle) {
            $q->whereRaw('LOWER(title) LIKE ?', ['%'.strtolower($saleTitle).'%']);
        }

        return $q->orderBy('expected_collect_date')->first();
    }

    private function findPendingSaleForClient(string $clientName, ?string $saleTitle): ?Sale
    {
        return $this->findSaleForPayment($clientName, $saleTitle, null);
    }

    private function resolveAccount(?string $accountName, Currency $currency): ?Account
    {
        if ($accountName) {
            return Account::whereRaw('LOWER(name) LIKE ?', ['%'.strtolower($accountName).'%'])->first();
        }

        $preferred = $currency === Currency::USD ? 'Payoneer' : 'Banco NUNA';
        return Account::whereRaw('LOWER(name) LIKE ?', ['%'.strtolower($preferred).'%'])->first()
            ?? Account::first();
    }

    private function parseDate(string $dateStr): string
    {
        $dateStr = strtolower(trim($dateStr));
        return match ($dateStr) {
            'hoy'       => now()->toDateString(),
            'mañana'    => now()->addDay()->toDateString(),
            'ayer'      => now()->subDay()->toDateString(),
            'lunes'     => now()->next('Monday')->toDateString(),
            'martes'    => now()->next('Tuesday')->toDateString(),
            'miércoles', 'miercoles' => now()->next('Wednesday')->toDateString(),
            'jueves'    => now()->next('Thursday')->toDateString(),
            'viernes'   => now()->next('Friday')->toDateString(),
            default     => rescue(
                fn () => Carbon::parse($dateStr)->toDateString(),
                now()->toDateString()
            ),
        };
    }

    // ── Construcción del intent (para confirmación) ───────────────────────────

    private function buildIntent(string $tool, array $input): array
    {
        $display = match ($tool) {
            'create_sale'      => $this->displaySale($input),
            'register_payment' => $this->displayPayment($input),
            'set_follow_up'    => $this->displayFollowUp($input),
            default            => ['title' => $tool, 'lines' => []],
        };

        return [
            'type'    => 'intent',
            'tool'    => $tool,
            'input'   => $input,
            'display' => $display,
        ];
    }

    private function displaySale(array $input): array
    {
        $saleType = ($input['sale_type'] ?? 'fixed') === 'hourly' ? 'Por hora' : 'Precio fijo';
        $currency = $input['currency'] ?? 'USD';
        $sym      = Currency::tryFrom($currency)?->symbol() ?? '$';

        $lines = [
            ['label' => 'Cliente',  'value' => $input['client_name'] . (($input['is_new_client'] ?? false) ? ' ✨ nuevo' : ' ✓')],
            ['label' => 'Título',   'value' => $input['title']],
            ['label' => 'Tipo',     'value' => $saleType],
            ['label' => 'Moneda',   'value' => $currency],
        ];

        if (($input['sale_type'] ?? 'fixed') === 'hourly') {
            $gross = round(($input['hourly_rate'] ?? 0) * ($input['hours'] ?? 0), 2);
            $lines[] = ['label' => 'Horas', 'value' => ($input['hours'] ?? 0) . ' h × ' . $sym . ($input['hourly_rate'] ?? 0)];
            $lines[] = ['label' => 'Total', 'value' => $sym . $gross, 'bold' => true];
        } else {
            $lines[] = ['label' => 'Monto', 'value' => $sym . number_format((float)($input['amount'] ?? 0), 2), 'bold' => true];
        }

        if (!empty($input['expected_collect_date'])) {
            $lines[] = ['label' => 'F. Cobro', 'value' => $input['expected_collect_date']];
        }

        return ['title' => '📝 Nueva Venta', 'lines' => $lines];
    }

    private function displayPayment(array $input): array
    {
        $currency = $input['currency'] ?? 'USD';
        $sym      = Currency::tryFrom($currency)?->symbol() ?? '$';

        return [
            'title' => '💰 Registrar Pago',
            'lines' => [
                ['label' => 'Cliente',    'value' => $input['client_name']],
                ['label' => 'Venta',      'value' => $input['sale_title'] ?? 'Venta pendiente'],
                ['label' => 'Monto',      'value' => $sym . number_format((float)($input['amount'] ?? 0), 2), 'bold' => true],
                ['label' => 'Moneda',     'value' => $currency],
                ['label' => 'Fecha',      'value' => $input['payment_date'] ?? 'hoy'],
                ['label' => 'Cuenta',     'value' => $input['account_name'] ?? 'por defecto'],
            ],
        ];
    }

    private function displayFollowUp(array $input): array
    {
        $type = ($input['type'] ?? 'collect') === 'followup' ? '📌 Seguimiento' : '📅 Fecha de Cobro';
        return [
            'title' => $type,
            'lines' => [
                ['label' => 'Cliente', 'value' => $input['client_name']],
                ['label' => 'Fecha',   'value' => $input['date'], 'bold' => true],
                ['label' => 'Nota',    'value' => $input['note'] ?? '—'],
            ],
        ];
    }

    // ── System prompt ─────────────────────────────────────────────────────────

    private function buildSystemPrompt(): string
    {
        $rate    = SystemSetting::current()->exchange_rate_usd_bob ?? 6.96;
        $clients = Client::select('id', 'name', 'currency_default', 'hourly_rate', 'origin')
            ->orderBy('name')->get();
        $accounts = Account::select('id', 'name')->get();
        $pending  = Sale::with('client')
            ->whereIn('payment_status', [PaymentStatus::Unpaid->value, PaymentStatus::Partial->value])
            ->orderBy('expected_collect_date')
            ->limit(30)->get();

        $clientsStr = $clients->map(fn ($c) => sprintf(
            '  [%d] %s | Moneda: %s | Tarifa/h: %s',
            $c->id,
            $c->name,
            $c->currency_default?->value ?? 'USD',
            $c->hourly_rate > 0 ? '$'.$c->hourly_rate.'/h' : 'no definida'
        ))->join("\n");

        $accountsStr = $accounts->map(fn ($a) => "  [{$a->id}] {$a->name}")->join("\n");

        $pendingStr = $pending->map(fn ($s) => sprintf(
            '  [%d] %s → %s | Saldo: %s%s',
            $s->id,
            $s->client?->name,
            $s->title,
            $s->currency?->symbol() ?? '$',
            number_format($s->balance, 2)
        ))->join("\n");

        return <<<PROMPT
Sos Nuna, el asistente inteligente del CRM financiero de la empresa. Mantenés una conversación natural en español, como un colega experto que entiende el contexto del negocio.

FECHA Y HORA ACTUAL: {$this->now()}
TIPO DE CAMBIO: 1 USD = {$rate} BOB

CLIENTES REGISTRADOS:
{$clientsStr}

CUENTAS DE COBRO DISPONIBLES:
{$accountsStr}

VENTAS PENDIENTES DE COBRO:
{$pendingStr}

CÓMO COMPORTARTE:
- Cuando el usuario dé suficiente información para una acción: usá la herramienta correspondiente (create_sale, register_payment, set_follow_up).
- Cuando falte información clave o el comando sea ambiguo: respondé con una pregunta corta y natural en español, SIN usar herramienta. Pedí solo lo que realmente falta.
- Usá el historial de la conversación para entender referencias como "él", "ese cliente", "la venta de antes", "como dijiste", etc.
- Si el usuario menciona un cliente de la lista, usá su ID y moneda preferida automáticamente.
- Para ventas por hora: calculá total = horas × tarifa. Para precio fijo: usá el monto directo.
- Para fechas relativas ("mañana", "el viernes"): devolvelas tal cual en el input de la herramienta, el sistema las interpreta.
- Respondé siempre en español, de forma amigable y concisa.
- NO pidas confirmación redundante si ya tenés toda la información — simplemente usá la herramienta.
PROMPT;
    }

    private function now(): string
    {
        return now()->format('d/m/Y H:i') . ' (' . now()->locale('es')->dayName . ')';
    }

    // ── Definición de herramientas para Claude ────────────────────────────────

    private function getTools(): array
    {
        return [
            [
                'name'        => 'create_sale',
                'description' => 'Crear una nueva venta en el CRM. Usar cuando el usuario quiera añadir una venta, proyecto, tarea o trabajo para un cliente.',
                'input_schema' => [
                    'type'       => 'object',
                    'properties' => [
                        'client_name'     => ['type' => 'string', 'description' => 'Nombre exacto o aproximado del cliente'],
                        'is_new_client'   => ['type' => 'boolean', 'description' => 'true si el cliente NO existe en la lista de clientes registrados'],
                        'client_currency' => ['type' => 'string', 'enum' => ['USD', 'BOB'], 'description' => 'Solo si is_new_client=true'],
                        'client_origin'   => ['type' => 'string', 'description' => 'Solo si is_new_client=true. Valores: manual_local, manual_internacional, upwork'],
                        'title'           => ['type' => 'string', 'description' => 'Título descriptivo del trabajo o proyecto'],
                        'sale_type'       => ['type' => 'string', 'enum' => ['fixed', 'hourly'], 'description' => 'fixed para precio único, hourly para horas trabajadas'],
                        'amount'          => ['type' => 'number', 'description' => 'Monto total si sale_type=fixed'],
                        'hours'           => ['type' => 'number', 'description' => 'Cantidad de horas si sale_type=hourly'],
                        'hourly_rate'     => ['type' => 'number', 'description' => 'Tarifa por hora si sale_type=hourly'],
                        'currency'        => ['type' => 'string', 'enum' => ['USD', 'BOB']],
                        'expected_collect_date' => ['type' => 'string', 'description' => 'Fecha esperada de cobro (ej: "viernes", "2026-03-15")'],
                    ],
                    'required' => ['client_name', 'title', 'sale_type', 'currency'],
                ],
            ],
            [
                'name'        => 'register_payment',
                'description' => 'Registrar un pago recibido de un cliente por una venta existente.',
                'input_schema' => [
                    'type'       => 'object',
                    'properties' => [
                        'client_name'  => ['type' => 'string'],
                        'sale_id'      => ['type' => 'integer', 'description' => 'ID de la venta si se identifica claramente'],
                        'sale_title'   => ['type' => 'string', 'description' => 'Título aproximado de la venta si hay varias pendientes'],
                        'amount'       => ['type' => 'number', 'description' => 'Monto del pago'],
                        'currency'     => ['type' => 'string', 'enum' => ['USD', 'BOB']],
                        'payment_date' => ['type' => 'string', 'description' => 'Fecha del pago (ej: "hoy", "ayer", "2026-03-01")'],
                        'account_name' => ['type' => 'string', 'description' => 'Nombre de la cuenta (Payoneer, Takenos, etc.)'],
                        'note'         => ['type' => 'string'],
                    ],
                    'required' => ['client_name', 'amount'],
                ],
            ],
            [
                'name'        => 'set_follow_up',
                'description' => 'Establecer fecha de cobro esperada o fecha de seguimiento para una venta pendiente.',
                'input_schema' => [
                    'type'       => 'object',
                    'properties' => [
                        'client_name' => ['type' => 'string'],
                        'sale_id'     => ['type' => 'integer'],
                        'sale_title'  => ['type' => 'string'],
                        'date'        => ['type' => 'string', 'description' => 'Fecha a programar'],
                        'type'        => ['type' => 'string', 'enum' => ['collect', 'followup'], 'description' => 'collect = fecha cobro, followup = nota de seguimiento'],
                        'note'        => ['type' => 'string'],
                    ],
                    'required' => ['client_name', 'date'],
                ],
            ],
        ];
    }
}
