<?php

namespace App\Services\Pagarme;

use App\Data\Pagarme\Recipient\BankAccountUpdateRequestData;
use App\Data\Pagarme\Recipient\Parts\Request\AddressData;
use App\Data\Pagarme\Recipient\Parts\Request\AutomaticAnticipationSettingsData;
use App\Data\Pagarme\Recipient\Parts\Request\BankAccountData;
use App\Data\Pagarme\Recipient\Parts\Request\PhoneData;
use App\Data\Pagarme\Recipient\Parts\Request\PhoneNumbersData;
use App\Data\Pagarme\Recipient\Parts\Request\RegisterInformationData;
use App\Data\Pagarme\Recipient\Parts\Request\TransferSettingsData;
use App\Data\Pagarme\Recipient\RecipientRequestData;
use App\Data\Pagarme\Recipient\RecipientResponseData;
use App\Models\Provider;
use App\Services\Pagarme\Concerns\FormatsPagarmeData;
use App\Services\Pagarme\Concerns\MocksPagarmeRequests;
use App\Services\Pagarme\Concerns\SendsPagarmeRequests;

class PagarmeRecipientService
{
    use FormatsPagarmeData;
    use MocksPagarmeRequests;
    use SendsPagarmeRequests;

    public function createRecipientForProvider(Provider $provider, array $data): string
    {
        if (! empty($provider->recipient_id)) {
            return $provider->recipient_id;
        }

        $metadata      = $data['recipient_metadata'] ?? [];
        $paymentMode   = $data['recipient_payment_mode'];
        $recipientCode = $provider->ensureGatewayCode();

        $addressParts = $this->extractAddressParts($data);

        $registerInformation = new RegisterInformationData(
            name:                   $data['recipient_name'],
            email:                  $data['recipient_email'],
            document:               $this->digits($data['recipient_document'] ?? null),
            type:                   $data['recipient_type'] ?? 'individual',
            birthdate:              $this->formatBirthdate($data['birth_date'] ?? null),
            monthlyIncome:          isset($data['monthly_income']) ? (int) $data['monthly_income'] : 1000,
            professionalOccupation: $data['professional_occupation'] ?? 'autonomo',

            phoneNumbers: new PhoneNumbersData(
                $this->buildRecipientPhone($data['phone'] ?? null),
            ),

            address: new AddressData(
                street:         $addressParts['street'],
                complementary:  $addressParts['complementary'],
                streetNumber:   $addressParts['street_number'],
                neighborhood:   $addressParts['neighborhood'],
                city:           $data['city']  ?? null,
                state:          $data['state'] ?? null,
                zipCode:        $this->digits($data['zip_code'] ?? null),
                referencePoint: $addressParts['reference_point'],
            ),
        );

        $defaultBankAccount = $this->buildRecipientBankAccount(
            $data['recipient_default_bank_account'],
        );

        $payload = new RecipientRequestData(
            code: $recipientCode,

            registerInformation: $registerInformation,
            defaultBankAccount:  $defaultBankAccount,

            transferSettings: new TransferSettingsData(
                transferEnabled:  false,
                transferInterval: 'Daily',
                transferDay:      0,
            ),

            automaticAnticipationSettings: new AutomaticAnticipationSettingsData(
                enabled: true,
                type:    '1025', // isso significa que vai ser feita de acordo com o delay (dias)
                delay:   1,
            ),
        );

        $bankAccountData = $payload->defaultBankAccount->toArray();

        if ($this->shouldMockPagarmeRequest()) {
            $recipientId = $this->mockPagarmeId('re', $provider->id);

            $provider->forceFill([
                'recipient_id'                   => $recipientId,
                'recipient_name'                 => $data['recipient_name'],
                'recipient_email'                => $data['recipient_email'],
                'recipient_description'          => $data['recipient_description'] ?? '',
                'recipient_document'             => $data['recipient_document'],
                'recipient_type'                 => $payload->registerInformation->type,
                'recipient_code'                 => $recipientCode,
                'recipient_payment_mode'         => $paymentMode,
                'recipient_default_bank_account' => $bankAccountData,

                'recipient_transfer_settings' => [
                    'transfer_enabled'  => false,
                    'transfer_interval' => 'daily',
                    'transfer_day'      => 0,
                ],

                'recipient_automatic_anticipation_settings' => [
                    'enabled' => true,
                    'type'    => 'full',
                    'delay'   => 1,
                ],

                'recipient_metadata' => array_merge($metadata, ['mocked' => true]),
            ])->save();

            return $recipientId;
        }

        $raw = $this->pagarmeRequest(
            method:         'POST',
            path:           '/recipients',
            payload:        $payload,
            idempotencyKey: $this->idempotencyKey($provider, '', $data),
            errorMessage:   'Erro ao criar recebedor no Pagar.me.',
        );

        $recipientData = RecipientResponseData::fromArray($raw);

        $recipientId = $recipientData->requireId();

        $provider->forceFill([
            'recipient_id'                   => $recipientId,
            'recipient_name'                 => $data['recipient_name'],
            'recipient_email'                => $data['recipient_email'],
            'recipient_description'          => $data['recipient_description'] ?? '',
            'recipient_document'             => $data['recipient_document'],
            'recipient_type'                 => $payload->registerInformation->type,
            'recipient_code'                 => $recipientCode,
            'recipient_payment_mode'         => $paymentMode,
            'recipient_default_bank_account' => $bankAccountData,

            'recipient_transfer_settings' => [
                'transfer_enabled'  => false,
                'transfer_interval' => 'daily',
                'transfer_day'      => 0,
            ],

            'recipient_automatic_anticipation_settings' => [
                'enabled' => true,
                'type'    => 'full',
                'delay'   => 1,
            ],

            'recipient_metadata' => $metadata,
        ])->save();

        return $recipientId;
    }

    public function updateDefaultBankAccount(Provider $provider, array $bankAccountData): Provider
    {
        $payload = new BankAccountUpdateRequestData(
            holderName:        $this->normalizeHolderName($bankAccountData['holder_name']),
            holderType:        $bankAccountData['holder_type'],
            holderDocument:    $this->digits($bankAccountData['holder_document']),
            bank:              $bankAccountData['bank'],
            branchNumber:      $bankAccountData['branch_number'],
            branchCheckDigit:  $bankAccountData['branch_check_digit'] ?? null,
            accountNumber:     $bankAccountData['account_number'],
            accountCheckDigit: $bankAccountData['account_check_digit'],
            type:              $bankAccountData['type'],
        );

        if ($this->shouldMockPagarmeRequest()) {
            $provider->forceFill([
                'recipient_default_bank_account' => $payload->toArray()['bank_account'],
            ])->save();

            return $provider->fresh();
        }

        $this->pagarmeRequest(
            method:         'PATCH',
            path:           "/recipients/{$provider->recipient_id}/default-bank-account",
            payload:        $payload,
            idempotencyKey: $this->idempotencyKey($provider, 'default-bank-account-'.sha1(json_encode($payload->toArray()))),
            errorMessage:   'Erro ao atualizar conta bancaria do recebedor no Pagar.me.',
        );

        $provider->forceFill([
            'recipient_default_bank_account' => $payload->toArray()['bank_account'],
        ])->save();

        return $provider->fresh();
    }

    //

    private function buildRecipientBankAccount(array $data): BankAccountData
    {
        return new BankAccountData(
            holderName:        $this->normalizeHolderName($data['holder_name']),
            holderType:        $data['holder_type'],
            holderDocument:    $this->digits($data['holder_document']),
            bank:              $data['bank'],
            branchNumber:      $data['branch_number'],
            branchCheckDigit:  $data['branch_check_digit'] ?? null,
            accountNumber:     $data['account_number'],
            accountCheckDigit: $data['account_check_digit'],
            type:              $data['type'],
        );
    }

    private function buildRecipientPhone(?string $phone): PhoneData
    {
        $digits = $this->digits($phone);

        if (strlen($digits) < 10) {
            return new PhoneData(
                ddd:    '11',
                number: '999999999',
                type:   'mobile',
            );
        }

        if (str_starts_with($digits, '55')) {
            $digits = substr($digits, 2);
        }

        return new PhoneData(
            ddd:    substr($digits, 0, 2),
            number: substr($digits, 2),
            type:   'mobile',
        );
    }

    // evita criacao duplica de recipient

    private function idempotencyKey(Provider $provider, string $suffix = '', array $data = []): string
    {
        $baseKey = $provider->idempotency_key;

        if (empty($baseKey)) {
            $provider->loadMissing('user');

            $baseKey = $this->pagarmeIdempotencyKey('recipient', [
                $data['recipient_email'] ?? $provider->recipient_email ?? $provider->user?->email ?? "provider-{$provider->id}",
                $this->customerDocument($data['recipient_document'] ?? $provider->recipient_document ?? $provider->document)
                    ?: "document-provider-{$provider->id}",
            ]);

            $provider->forceFill(['idempotency_key' => $baseKey])->save();
        }

        return $suffix ? "{$baseKey}-{$suffix}" : $baseKey;
    }
}
