<?php

declare(strict_types=1);

namespace App\Services;

use App\Core\FileStore;

class WithdrawalService
{
    private FileStore $withdrawals;
    private CommissionService $commissions;
    private ActivityLogService $logs;

    public function __construct()
    {
        $this->withdrawals = new FileStore(storage_path('data/withdrawals.json'));
        $this->commissions = new CommissionService();
        $this->logs = new ActivityLogService();
    }

    public function wallet(string $sellerType, int $sellerId): array
    {
        $commissions = $this->commissions->all([
            'seller_type' => $sellerType,
            'seller_id' => $sellerId,
        ]);
        $totals = $this->commissions->totals($commissions);
        $withdrawals = $this->forSeller($sellerType, $sellerId);

        $pending = $this->sumByStatus($withdrawals, ['requested', 'approved']);
        $paid = $this->sumByStatus($withdrawals, ['paid']);
        $rejected = $this->sumByStatus($withdrawals, ['rejected']);
        $available = max(0, (float) $totals['seller'] - $pending - $paid);

        return [
            'gross' => (float) $totals['gross'],
            'platform' => (float) $totals['platform'],
            'seller' => (float) $totals['seller'],
            'available' => $available,
            'pending' => $pending,
            'paid' => $paid,
            'rejected' => $rejected,
            'withdrawals_count' => count($withdrawals),
            'sales_count' => (int) $totals['count'],
        ];
    }

    public function forSeller(string $sellerType, int $sellerId): array
    {
        $items = array_filter($this->withdrawals->all(), function (array $withdrawal) use ($sellerType, $sellerId): bool {
            return ($withdrawal['seller_type'] ?? '') === $sellerType
                && (int) ($withdrawal['seller_id'] ?? 0) === $sellerId;
        });

        usort($items, fn (array $a, array $b): int => strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''));

        return array_values($items);
    }

    public function all(array $filters = []): array
    {
        $items = $this->withdrawals->all();
        $status = (string) ($filters['status'] ?? '');
        $search = strtolower(trim((string) ($filters['search'] ?? '')));

        if ($status !== '') {
            $items = array_filter($items, fn (array $item): bool => ($item['status'] ?? '') === $status);
        }

        if ($search !== '') {
            $items = array_filter($items, function (array $item) use ($search): bool {
                $haystack = strtolower(
                    ($item['reference'] ?? '') . ' ' .
                    ($item['seller_name'] ?? '') . ' ' .
                    ($item['seller_email'] ?? '') . ' ' .
                    ($item['destination_name'] ?? '') . ' ' .
                    ($item['destination_number'] ?? '')
                );

                return str_contains($haystack, $search);
            });
        }

        usort($items, fn (array $a, array $b): int => strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''));

        return array_values($items);
    }

    public function request(array $data, array $user): array
    {
        $seller = $this->sellerForUser($user);

        if (!$seller) {
            return ['ok' => false, 'error' => 'Apenas instrutores, instituicoes e administradores podem pedir levantamento.'];
        }

        $amount = (float) str_replace(',', '.', (string) ($data['amount'] ?? 0));
        $wallet = $this->wallet($seller['type'], (int) $user['id']);

        if ($amount < 100) {
            return ['ok' => false, 'error' => 'O valor minimo para levantamento e 100 MZN.'];
        }

        if ($amount > (float) $wallet['available']) {
            return ['ok' => false, 'error' => 'Saldo disponivel insuficiente.'];
        }

        $method = (string) ($data['method'] ?? 'mpesa');

        if (!in_array($method, ['mpesa', 'emola', 'mkesh', 'bank_transfer'], true)) {
            $method = 'mpesa';
        }

        $destinationName = trim((string) ($data['destination_name'] ?? ''));
        $destinationNumber = trim((string) ($data['destination_number'] ?? ''));

        if ($destinationName === '' || $destinationNumber === '') {
            return ['ok' => false, 'error' => 'Informe o titular e numero/conta de destino.'];
        }

        $withdrawal = $this->withdrawals->insert([
            'reference' => 'WDR-' . date('Ymd') . '-' . strtoupper(substr(bin2hex(random_bytes(3)), 0, 6)),
            'seller_type' => $seller['type'],
            'seller_id' => (int) $user['id'],
            'seller_name' => $user['name'] ?? 'Vendedor',
            'seller_email' => $user['email'] ?? '',
            'amount' => round($amount, 2),
            'currency' => 'MZN',
            'method' => $method,
            'destination_name' => $destinationName,
            'destination_number' => $destinationNumber,
            'notes' => trim((string) ($data['notes'] ?? '')),
            'status' => 'requested',
            'admin_note' => '',
            'approved_by' => null,
            'approved_at' => null,
            'rejected_by' => null,
            'rejected_at' => null,
            'paid_by' => null,
            'paid_at' => null,
            'created_at' => date('c'),
            'updated_at' => date('c'),
        ]);

        $this->logs->log('withdrawal_requested', "Levantamento {$withdrawal['reference']} solicitado.", (int) $user['id']);

        return ['ok' => true, 'withdrawal' => $withdrawal];
    }

    public function changeStatus(int $id, string $status, array $admin, string $note = ''): array
    {
        if (($admin['role'] ?? '') !== 'admin') {
            return ['ok' => false, 'error' => 'Apenas administradores podem gerir levantamentos.'];
        }

        if (!in_array($status, ['approved', 'rejected', 'paid'], true)) {
            return ['ok' => false, 'error' => 'Estado invalido.'];
        }

        $records = $this->withdrawals->all();
        $updated = null;

        foreach ($records as $index => $withdrawal) {
            if ((int) ($withdrawal['id'] ?? 0) !== $id) {
                continue;
            }

            if (($withdrawal['status'] ?? '') === 'paid') {
                return ['ok' => false, 'error' => 'Levantamento ja foi pago.'];
            }

            if ($status === 'paid' && !in_array($withdrawal['status'] ?? '', ['approved', 'requested'], true)) {
                return ['ok' => false, 'error' => 'Este levantamento nao pode ser marcado como pago.'];
            }

            $changes = [
                'status' => $status,
                'admin_note' => trim($note),
                'updated_at' => date('c'),
            ];

            if ($status === 'approved') {
                $changes['approved_by'] = (int) $admin['id'];
                $changes['approved_at'] = date('c');
            }

            if ($status === 'rejected') {
                $changes['rejected_by'] = (int) $admin['id'];
                $changes['rejected_at'] = date('c');
            }

            if ($status === 'paid') {
                $changes['paid_by'] = (int) $admin['id'];
                $changes['paid_at'] = date('c');
            }

            $records[$index] = array_merge($withdrawal, $changes);
            $updated = $records[$index];
            break;
        }

        if (!$updated) {
            return ['ok' => false, 'error' => 'Pedido de levantamento nao encontrado.'];
        }

        $this->withdrawals->save($records);
        $this->logs->log('withdrawal_status_changed', "Levantamento {$updated['reference']} alterado para {$status}.", (int) $admin['id']);

        return ['ok' => true, 'withdrawal' => $updated];
    }

    public function sellerForUser(array $user): ?array
    {
        return match ($user['role'] ?? '') {
            'institution' => ['type' => 'institution'],
            'admin' => ['type' => 'platform'],
            'instructor' => ['type' => 'instructor'],
            default => null,
        };
    }

    public function statusLabel(string $status): string
    {
        return [
            'requested' => 'Solicitado',
            'approved' => 'Aprovado',
            'rejected' => 'Rejeitado',
            'paid' => 'Pago',
        ][$status] ?? ucfirst($status);
    }

    public function methodLabel(string $method): string
    {
        return [
            'mpesa' => 'M-Pesa',
            'emola' => 'e-Mola',
            'mkesh' => 'mKesh',
            'bank_transfer' => 'Transferencia bancaria',
        ][$method] ?? ucfirst($method);
    }

    private function sumByStatus(array $withdrawals, array $statuses): float
    {
        return array_sum(array_map(
            fn (array $withdrawal): float => (float) ($withdrawal['amount'] ?? 0),
            array_filter($withdrawals, fn (array $withdrawal): bool => in_array($withdrawal['status'] ?? '', $statuses, true))
        ));
    }
}
