<?php

namespace App\Services\Pagarme;

use App\Data\Pagarme\Transfer\TransferRequestData;
use App\Data\Pagarme\Transfer\TransferResponseData;
use App\Models\Provider;
use App\Models\ProviderWithdrawal;
use App\Services\Pagarme\Concerns\MocksPagarmeRequests;
use App\Services\Pagarme\Concerns\SendsPagarmeRequests;
use Carbon\Carbon;

class PagarmeTransferService
{
    use MocksPagarmeRequests;
    use SendsPagarmeRequests;

    public function createTransfer(int $amountInCents, string $recipientId, string $idempotencyKey): TransferResponseData
    {
        if ($this->shouldMockTransferRequest()) {
            return $this->mockTransferResponse(
                amountInCents:  $amountInCents,
                recipientId:    $recipientId,
                idempotencyKey: $idempotencyKey,
            );
        }

        return TransferResponseData::fromArray($this->pagarmeRequest(
            method: 'POST',
            path:   '/transfers',

            payload: new TransferRequestData(
                amount:      $amountInCents,
                recipientId: $recipientId,
            ),

            idempotencyKey: $idempotencyKey,
            errorMessage:   'Erro ao criar transferencia (saque) no Pagar.me.',
        ));
    }

    public function getTransfer(string $transferId): TransferResponseData
    {
        if ($this->shouldMockTransferRequest()) {
            return $this->mockTransferLookupResponse($transferId);
        }

        return TransferResponseData::fromArray($this->pagarmeRequest(
            method:         'GET',
            path:           "/transfers/{$transferId}",
            payload:        [],
            idempotencyKey: "get-transfer-{$transferId}",
            errorMessage:   'Erro ao consultar transferencia no Pagar.me.',
        ));
    }

    //

    public function getRecipientBalance(string $recipientId): array
    {
        if ($this->shouldMockPagarmeRequest()) {
            return [
                'recipient_id' => $recipientId,
                'available'    => ['amount' => 100000],
                'waiting_funds'=> ['amount' => 0],
                'transferred'  => ['amount' => 0],
                'mocked'       => true,
            ];
        }

        return $this->pagarmeRequest(
            method:         'GET',
            path:           "/recipients/{$recipientId}/balance",
            payload:        [],
            idempotencyKey: "get-recipient-balance-{$recipientId}",
            errorMessage:   'Erro ao consultar saldo do recebedor no Pagar.me.',
        );
    }

    //

    private function mockTransferId(string $idempotencyKey): string
    {
        return $this->mockPagarmeId('tr', $idempotencyKey);
    }

    private function shouldMockTransferRequest(): bool
    {
        return $this->shouldMockPagarmeRequest();
    }

    //

    private function mockTransferLookupResponse(string $transferId): TransferResponseData
    {
        $withdrawal = ProviderWithdrawal::query()->where('transfer_id', $transferId)->first();

        if ($withdrawal?->gateway_payload) {
            return TransferResponseData::fromArray($withdrawal->gateway_payload);
        }

        $createdAt = Carbon::now();

        return new TransferResponseData(
            id:                   $transferId,
            amount:               $withdrawal ? (int) round((float) $withdrawal->gross_amount * 100) : 0,
            type:                 $withdrawal?->type           ?? 'credito_em_conta',
            status:               $withdrawal?->status?->value ?? 'pending_transfer',
            fee:                  $withdrawal ? (int) round((float) $withdrawal->gateway_fee_amount * 100) : 0,
            fundingDate:          $withdrawal?->completed_at?->toISOString(),
            fundingEstimatedDate: $createdAt->copy()->addWeekday()->toISOString(),
            bankAccount:          $withdrawal?->bank_account,
            bankResponse:         $withdrawal?->bank_response,
            createdAt:            $withdrawal?->created_at?->toISOString() ?? $createdAt->toISOString(),

            metadata: $withdrawal?->metadata ?? [
                'mocked'      => true,
                'environment' => app()->environment(),
            ],
        );
    }

    private function mockTransferResponse(int $amountInCents, string $recipientId, string $idempotencyKey): TransferResponseData
    {
        $provider  = Provider::query()->where('recipient_id', $recipientId)->first();
        $createdAt = Carbon::now();

        return new TransferResponseData(
            id:                   $this->mockTransferId($idempotencyKey),
            amount:               $amountInCents,
            type:                 'credito_em_conta',
            status:               'pending_transfer',
            fee:                  0,
            fundingDate:          null,
            fundingEstimatedDate: $createdAt->copy()->addWeekday()->toISOString(),
            bankAccount:          $provider?->recipient_default_bank_account,
            bankResponse:         null,
            createdAt:            $createdAt->toISOString(),

            metadata: [
                'mocked'          => true,
                'environment'     => app()->environment(),
                'recipient_id'    => $recipientId,
                'idempotency_key' => $idempotencyKey,
                'provider_id'     => $provider?->id,
            ],
        );
    }
}
