<?php

namespace App\Console\Commands;

use App\Enums\Currency;
use App\Enums\PaymentStatus;
use App\Enums\SaleOrigin;
use App\Models\Payment;
use App\Models\Sale;
use App\Services\PaymentService;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class ImportUpworkPayments extends Command
{
    protected $signature   = 'upwork:import-payments {csv : Ruta al CSV del transaction report}';
    protected $description = 'Crea Payment records para ventas de Upwork y actualiza platform_fee desde el transaction report CSV.';

    public function __construct(private PaymentService $paymentService)
    {
        parent::__construct();
    }

    public function handle(): int
    {
        $csvPath = $this->argument('csv');

        if (! file_exists($csvPath)) {
            $this->error("Archivo no encontrado: $csvPath");
            return 1;
        }

        $rows   = $this->parseCsv($csvPath);
        $groups = $this->groupTransactions($rows);

        $this->info(sprintf('Transacciones Hourly encontradas: %d', count($groups)));

        $payoneerAccountId = 4;
        $adminUserId       = \App\Models\User::where('email', 'admin@nuna.com')->value('id') ?? 1;

        $created = 0;
        $skipped = 0;

        foreach ($groups as $txId => $group) {
            $gross  = $group['gross'];
            $fee    = $group['fee'];
            $net    = round($gross - $fee, 2);
            $date   = $group['date'];
            $client = $group['client'];

            $sale = $this->findSale($client, $gross);

            if (! $sale) {
                $this->warn("  [SKIP] No se encontró venta para: $client | \$$gross");
                $skipped++;
                continue;
            }

            if (Payment::where('note', 'like', "%TxID:$txId%")->exists()) {
                $this->line("  [DUP]  Ya existe pago TxID:$txId — {$sale->client->name}");
                $skipped++;
                continue;
            }

            DB::transaction(function () use ($sale, $gross, $fee, $net, $date, $txId, $payoneerAccountId, $adminUserId) {
                Sale::withoutEvents(fn () => $sale->update(['platform_fee' => $fee]));

                Payment::create([
                    'sale_id'            => $sale->id,
                    'account_id'         => $payoneerAccountId,
                    'created_by_user_id' => $adminUserId,
                    'payment_date'       => $date,
                    'amount'             => $net,
                    'currency'           => Currency::USD,
                    'fee_amount'         => 0.00,
                    'net_amount'         => $net,
                    'note'               => "Upwork TxID:$txId — Bruto: \${$gross} | Fee Upwork: \${$fee}",
                ]);

                $this->paymentService->recalculateSale($sale->fresh());
            });

            $this->info("  [OK]   {$sale->client->name} | Bruto: \${$gross} | Fee: \${$fee} | Neto: \${$net}");
            $created++;
        }

        $this->fixOrphanPaidSales($payoneerAccountId, $adminUserId);

        $this->info("\n✓ Pagos creados desde CSV: $created | Omitidos: $skipped");
        return 0;
    }

    private function parseCsv(string $path): array
    {
        $rows   = [];
        $handle = fopen($path, 'r');
        fgetcsv($handle); // header

        while (($row = fgetcsv($handle)) !== false) {
            if (count($row) < 15) continue;
            $rows[] = array_combine([
                'date', 'tx_id', 'tx_type', 'summary', 'summary_details',
                'desc1', 'desc2', 'desc3', 'agency', 'freelancer',
                'client', 'account', 'po', 'ref_id', 'amount',
                'amount_local', 'currency', 'balance', 'payment_method',
            ], array_pad($row, 19, ''));
        }
        fclose($handle);

        return $rows;
    }

    private function groupTransactions(array $rows): array
    {
        $groups = [];
        $fees   = [];  // tx_id => fee_amount

        // Primera pasada: recopilar Hourly y Service Fee por separado
        foreach ($rows as $row) {
            $type = trim($row['tx_type']);
            $txId = trim($row['tx_id']);

            if ($type === 'Hourly') {
                $groups[$txId] = [
                    'gross'  => (float) $row['amount'],
                    'fee'    => 0.0,
                    'date'   => Carbon::parse($row['date'])->toDateString(),
                    'client' => trim($row['client']),
                ];
            }

            // Service Fee comparte el mismo tx_id que su Hourly correspondiente
            if ($type === 'Service Fee') {
                $fees[$txId] = abs((float) $row['amount']);
            }
        }

        // Segunda pasada: asignar fees a sus grupos Hourly
        foreach ($fees as $txId => $feeAmount) {
            if (isset($groups[$txId])) {
                $groups[$txId]['fee'] = $feeAmount;
            }
        }

        return array_filter($groups, fn ($g) => ($g['gross'] ?? 0) > 0);
    }

    private function findSale(string $clientName, float $gross): ?Sale
    {
        return Sale::where('origin', SaleOrigin::Upwork->value)
            ->whereBetween('gross_amount', [$gross - 0.05, $gross + 0.05])
            ->whereHas('client', fn ($q) => $q->where('name', 'like', '%' . $this->shortClientName($clientName) . '%'))
            ->whereDoesntHave('payments')
            ->orderBy('sale_date', 'asc')
            ->first();
    }

    private function shortClientName(string $name): string
    {
        $words = explode(' ', trim($name));
        return implode(' ', array_slice($words, 0, 2));
    }

    /**
     * Para ventas de Upwork "paid" que no estaban en el CSV (ej. Florencia, pagos fijos).
     * Asume 10% de fee de Upwork.
     */
    private function fixOrphanPaidSales(int $accountId, int $userId): void
    {
        $orphans = Sale::where('origin', SaleOrigin::Upwork->value)
            ->where('payment_status', PaymentStatus::Paid->value)
            ->whereDoesntHave('payments')
            ->get();

        if ($orphans->isEmpty()) return;

        $this->info("\nVentas Upwork 'paid' sin pago en CSV (se asume 10% fee):");

        foreach ($orphans as $sale) {
            $fee  = round((float) $sale->gross_amount * 0.10, 2);
            $net  = round((float) $sale->gross_amount - $fee, 2);
            $date = $sale->sale_date?->toDateString() ?? now()->toDateString();

            DB::transaction(function () use ($sale, $fee, $net, $date, $accountId, $userId) {
                Sale::withoutEvents(fn () => $sale->update(['platform_fee' => $fee]));

                Payment::create([
                    'sale_id'            => $sale->id,
                    'account_id'         => $accountId,
                    'created_by_user_id' => $userId,
                    'payment_date'       => $date,
                    'amount'             => $net,
                    'currency'           => Currency::USD,
                    'fee_amount'         => 0.00,
                    'net_amount'         => $net,
                    'note'               => "Upwork pago estimado (10% fee) — Bruto: \${$sale->gross_amount}",
                ]);

                $this->paymentService->recalculateSale($sale->fresh());
            });

            $this->warn("  [AUTO] {$sale->client->name} | Bruto: \${$sale->gross_amount} | Fee: \${$fee} | Neto: \${$net}");
        }
    }
}
