<?php

namespace App\Jobs;

use App\Models\Notificacao;
use App\Models\Transacao;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class ProcessarNotificacao implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $timeout = 7200;
    public $tries = 3;
    public $backoff = [60, 300, 600];
    public $maxExceptions = 3;

    private Notificacao $notificacao;

    public function __construct(
        public int $notificacaoId
    ) {}

    public function handle(): void
    {
        try {
            $this->notificacao = Notificacao::with(['credencial', 'produto'])->findOrFail($this->notificacaoId);
        } catch (\Exception $e) {
            \Log::error('Erro ao carregar notificação', [
                'notificacao_id' => $this->notificacaoId,
                'error' => $e->getMessage()
            ]);
            throw $e;
        }

        $tokenDebug = $this->notificacao->credencial?->token_umtify;

        \Log::info('Iniciando processamento de notificação', [
            'notificacao_id' => $this->notificacao->id,
            'credencial_id' => $this->notificacao->credencial_id,
            'token_carregado' => $this->notificacao->credencial ? 'SIM' : 'NÃO',
            'token_value' => $tokenDebug ?? 'NULL',
            'token_length' => strlen($tokenDebug ?? ''),
            'quantidade_transacoes' => $this->notificacao->quantidade_transacoes,
            'metodo_pagamento' => $this->notificacao->metodo_pagamento,
            'tempo' => $this->notificacao->tempo_valor . ' ' . $this->notificacao->tempo_unidade,
            'taxa_aprovacao' => $this->notificacao->taxa_aprovacao
        ]);

        $this->notificacao->update([
            'status' => 'processando',
            'iniciada_em' => now(),
        ]);

        $tempoTotalSegundos = $this->calcularTempoTotalSegundos();
        \Log::info('Tempo total calculado', [
            'notificacao_id' => $this->notificacao->id,
            'tempo_total_segundos' => $tempoTotalSegundos
        ]);

        $transacoes = $this->criarTransacoes($tempoTotalSegundos);
        \Log::info('Transações criadas', [
            'notificacao_id' => $this->notificacao->id,
            'total_transacoes' => count($transacoes)
        ]);

        usort($transacoes, fn($a, $b) => $a['tempo_disparo'] <=> $b['tempo_disparo']);

        \Log::info('Fase 1: Enviando todas as transações', [
            'notificacao_id' => $this->notificacao->id
        ]);

        $transacoesAprovadas = [];

        foreach ($transacoes as $index => $transacaoData) {
            $this->notificacao->refresh();

            if ($this->notificacao->pausada) {
                \Log::info('Notificação pausada pelo usuário', [
                    'notificacao_id' => $this->notificacao->id
                ]);
                return;
            }

            \Log::info('Processando transação', [
                'notificacao_id' => $this->notificacao->id,
                'transacao_numero' => $index + 1,
                'total' => count($transacoes),
                'tempo_disparo' => $transacaoData['tempo_disparo']
            ]);

            if ($transacaoData['tempo_disparo'] > 0) {
                sleep($transacaoData['tempo_disparo']);
            }

            $this->notificacao->refresh();

            if ($this->notificacao->pausada) {
                \Log::info('Notificação pausada pelo usuário', [
                    'notificacao_id' => $this->notificacao->id
                ]);
                return;
            }

            $transacao = Transacao::find($transacaoData['id']);

            if ($this->notificacao->metodo_pagamento === 'credit_card') {
                $this->enviarTransacaoCartao($transacao);
            } else {
                $this->enviarTransacaoPix($transacao);

                if ($transacao->sera_aprovada) {
                    $transacoesAprovadas[] = [
                        'id' => $transacao->id,
                        'tempo_aprovacao' => rand(10, 30),
                    ];
                }
            }
        }

        if ($this->notificacao->metodo_pagamento === 'pix' && count($transacoesAprovadas) > 0) {
            \Log::info('Fase 2: Processando aprovações de PIX', [
                'notificacao_id' => $this->notificacao->id,
                'total_aprovacoes' => count($transacoesAprovadas)
            ]);

            usort($transacoesAprovadas, fn($a, $b) => $a['tempo_aprovacao'] <=> $b['tempo_aprovacao']);

            foreach ($transacoesAprovadas as $index => $aprovacaoData) {
                $this->notificacao->refresh();

                if ($this->notificacao->pausada) {
                    \Log::info('Notificação pausada durante aprovações PIX', [
                        'notificacao_id' => $this->notificacao->id
                    ]);
                    return;
                }

                if ($aprovacaoData['tempo_aprovacao'] > 0) {
                    \Log::info('Aguardando para aprovar PIX', [
                        'notificacao_id' => $this->notificacao->id,
                        'aprovacao_numero' => $index + 1,
                        'total' => count($transacoesAprovadas),
                        'tempo_espera' => $aprovacaoData['tempo_aprovacao'] . ' segundos'
                    ]);
                    sleep($aprovacaoData['tempo_aprovacao']);
                }

                $this->notificacao->refresh();

                if ($this->notificacao->pausada) {
                    \Log::info('Notificação pausada durante aprovações PIX', [
                        'notificacao_id' => $this->notificacao->id
                    ]);
                    return;
                }

                $transacao = Transacao::find($aprovacaoData['id']);
                $this->aprovarTransacaoPix($transacao);
            }
        }

        \Log::info('Processamento de notificação concluído', [
            'notificacao_id' => $this->notificacao->id,
            'transacoes_enviadas' => $this->notificacao->transacoes_enviadas,
            'transacoes_aprovadas' => $this->notificacao->transacoes_aprovadas,
            'transacoes_reprovadas' => $this->notificacao->transacoes_reprovadas
        ]);

        $this->notificacao->update([
            'status' => 'concluida',
            'concluida_em' => now(),
        ]);
    }

    private function calcularTempoTotalSegundos(): int
    {
        if ($this->notificacao->tempo_unidade === 'horas') {
            return $this->notificacao->tempo_valor * 3600;
        }
        return $this->notificacao->tempo_valor * 60;
    }

    private function criarTransacoes(int $tempoTotalSegundos): array
    {
        $quantidade = $this->notificacao->quantidade_transacoes;
        $taxaAprovacao = $this->notificacao->taxa_aprovacao ?? 100;

        $quantidadeAprovadas = (int) ceil(($quantidade * $taxaAprovacao) / 100);
        $indices = range(0, $quantidade - 1);
        shuffle($indices);
        $indicesAprovados = array_slice($indices, 0, $quantidadeAprovadas);

        $temposDisparo = $this->gerarTemposAleatorios($quantidade, $tempoTotalSegundos);

        $transacoes = [];
        $tempoAcumulado = 0;

        for ($i = 0; $i < $quantidade; $i++) {
            $seraAprovada = in_array($i, $indicesAprovados);

            $transacao = Transacao::create([
                'notificacao_id' => $this->notificacao->id,
                'order_id' => Str::uuid(),
                'status_envio' => 'pendente',
                'sera_aprovada' => $seraAprovada,
                'tempo_disparo_segundos' => $temposDisparo[$i],
            ]);

            $tempoDisparo = $temposDisparo[$i] - $tempoAcumulado;
            $tempoAcumulado = $temposDisparo[$i];

            $transacoes[] = [
                'id' => $transacao->id,
                'tempo_disparo' => max(0, $tempoDisparo),
            ];
        }

        return $transacoes;
    }

    private function gerarTemposAleatorios(int $quantidade, int $tempoTotal): array
    {
        if ($quantidade === 1) {
            return [0];
        }

        $tempos = [0];
        $tempoRestante = $tempoTotal;

        for ($i = 1; $i < $quantidade; $i++) {
            $tempoMinimo = (int) ($tempoRestante / ($quantidade - $i) * 0.3);
            $tempoMaximo = (int) ($tempoRestante / ($quantidade - $i) * 1.7);

            $tempoMaximo = min($tempoMaximo, $tempoRestante - ($quantidade - $i - 1));

            $intervalo = rand($tempoMinimo, max($tempoMinimo, $tempoMaximo));
            $tempos[] = $tempos[$i - 1] + $intervalo;
            $tempoRestante -= $intervalo;
        }

        return $tempos;
    }

    private function enviarTransacaoCartao(Transacao $transacao): void
    {
        try {
            $payload = $this->montarPayload($transacao, 'paid', now(), now());

            $token = $this->notificacao->credencial->token_umtify;

            \Log::info('Enviando transação CARTÃO para Utmify', [
                'order_id' => $transacao->order_id,
                'notificacao_id' => $this->notificacao->id,
                'credencial_existe' => $this->notificacao->credencial ? 'SIM' : 'NÃO',
                'token_value' => $token ?? 'NULL',
                'token_length' => strlen($token ?? ''),
                'payload' => $payload
            ]);

            $response = Http::withHeaders([
                'x-api-token' => $token,
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ])->post('https://api.utmify.com.br/api-credentials/orders', $payload);

            \Log::info('Resposta da API Utmify - CARTÃO', [
                'order_id' => $transacao->order_id,
                'status' => $response->status(),
                'body' => $response->body()
            ]);

            $transacao->update([
                'status_envio' => 'paid',
                'enviada_em' => now(),
                'aprovada_em' => now(),
                'resposta_api' => $response->body(),
            ]);

            $this->notificacao->increment('transacoes_enviadas');
            $this->notificacao->increment('transacoes_aprovadas');

        } catch (\Exception $e) {
            \Log::error('Erro ao enviar transação CARTÃO', [
                'order_id' => $transacao->order_id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            $transacao->update([
                'status_envio' => 'erro',
                'erro' => $e->getMessage(),
            ]);
        }
    }

    private function enviarTransacaoPix(Transacao $transacao): void
    {
        try {
            $createdAt = now();

            $payloadWaiting = $this->montarPayload($transacao, 'waiting_payment', $createdAt);

            $token = $this->notificacao->credencial->token_umtify;

            \Log::info('Enviando transação PIX PENDENTE para Utmify', [
                'order_id' => $transacao->order_id,
                'notificacao_id' => $this->notificacao->id,
                'sera_aprovada' => $transacao->sera_aprovada,
                'credencial_existe' => $this->notificacao->credencial ? 'SIM' : 'NÃO',
                'token_value' => $token ?? 'NULL',
                'token_length' => strlen($token ?? ''),
                'payload' => $payloadWaiting
            ]);

            $response = Http::withHeaders([
                'x-api-token' => $token,
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ])->post('https://api.utmify.com.br/api-credentials/orders', $payloadWaiting);

            \Log::info('Resposta da API Utmify - PIX PENDENTE', [
                'order_id' => $transacao->order_id,
                'status' => $response->status(),
                'body' => $response->body()
            ]);

            $transacao->update([
                'status_envio' => 'waiting_payment',
                'enviada_em' => now(),
                'resposta_api' => $response->body(),
            ]);

            $this->notificacao->increment('transacoes_enviadas');

            if (!$transacao->sera_aprovada) {
                \Log::info('PIX não será aprovado', [
                    'order_id' => $transacao->order_id
                ]);
                $this->notificacao->increment('transacoes_reprovadas');
            }

        } catch (\Exception $e) {
            \Log::error('Erro ao enviar transação PIX PENDENTE', [
                'order_id' => $transacao->order_id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            $transacao->update([
                'status_envio' => 'erro',
                'erro' => $e->getMessage(),
            ]);
        }
    }

    private function aprovarTransacaoPix(Transacao $transacao): void
    {
        try {
            $createdAt = $transacao->enviada_em;
            $approvedDate = now();

            $payloadPaid = $this->montarPayload($transacao, 'paid', $createdAt, $approvedDate);

            $token = $this->notificacao->credencial->token_umtify;

            \Log::info('Enviando transação PIX PAGO para Utmify', [
                'order_id' => $transacao->order_id,
                'credencial_existe' => $this->notificacao->credencial ? 'SIM' : 'NÃO',
                'token_value' => $token ?? 'NULL',
                'payload' => $payloadPaid
            ]);

            $response = Http::withHeaders([
                'x-api-token' => $token,
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ])->post('https://api.utmify.com.br/api-credentials/orders', $payloadPaid);

            \Log::info('Resposta da API Utmify - PIX PAGO', [
                'order_id' => $transacao->order_id,
                'status' => $response->status(),
                'body' => $response->body()
            ]);

            $transacao->update([
                'status_envio' => 'paid',
                'aprovada_em' => now(),
                'resposta_api' => $response->body(),
            ]);

            $this->notificacao->increment('transacoes_aprovadas');

        } catch (\Exception $e) {
            \Log::error('Erro ao aprovar transação PIX', [
                'order_id' => $transacao->order_id,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            $transacao->update([
                'status_envio' => 'erro',
                'erro' => $e->getMessage(),
            ]);
        }
    }

    private function montarPayload(Transacao $transacao, string $status, $createdAt, $approvedDate = null): array
    {
        $produto = $this->notificacao->produto;

        return [
            'orderId' => $transacao->order_id,
            'platform' => 'DispararPro',
            'paymentMethod' => $this->notificacao->metodo_pagamento,
            'status' => $status,
            'createdAt' => $createdAt->setTimezone('UTC')->format('Y-m-d H:i:s'),
            'approvedDate' => $approvedDate ? $approvedDate->setTimezone('UTC')->format('Y-m-d H:i:s') : null,
            'refundedAt' => null,
            'customer' => [
                'name' => $this->gerarNomeAleatorio(),
                'email' => $this->gerarEmailAleatorio(),
                'phone' => $this->gerarTelefoneAleatorio(),
                'document' => $this->gerarDocumentoAleatorio(),
                'country' => 'BR',
                'ip' => $this->gerarIpAleatorio(),
            ],
            'products' => [
                [
                    'id' => (string) $produto->id,
                    'name' => $produto->nome,
                    'planId' => null,
                    'planName' => null,
                    'quantity' => 1,
                    'priceInCents' => (int) ($produto->preco * 100),
                ],
            ],
            'trackingParameters' => [
   				 'src' => null,
    			 'sck' => null,
    			 'utm_source' => 'facebook',
    		     'utm_campaign' => 'nome_da_campanha',
    			 'utm_medium' => 'paid_social',
    			 'utm_content' => 'nome_do_anuncio',
    			 'utm_term' => 'publico',
				],
            'commission' => [
                'totalPriceInCents' => (int) ($produto->preco * 100),
                'gatewayFeeInCents' => (int) ($produto->preco * 100 * 0.05),
                'userCommissionInCents' => (int) ($produto->preco * 100 * 0.95),
            ],
            'isTest' => false,
        ];
    }

    private function gerarNomeAleatorio(): string
    {
        $nomes = ['João', 'Maria', 'Pedro', 'Ana', 'Carlos', 'Juliana', 'Lucas', 'Fernanda', 'Rafael', 'Paula'];
        $sobrenomes = ['Silva', 'Santos', 'Oliveira', 'Souza', 'Costa', 'Pereira', 'Rodrigues', 'Almeida', 'Nascimento', 'Lima'];

        return $nomes[array_rand($nomes)] . ' ' . $sobrenomes[array_rand($sobrenomes)] . ' ' . $sobrenomes[array_rand($sobrenomes)];
    }

    private function gerarEmailAleatorio(): string
    {
        $prefixos = ['user', 'cliente', 'comprador', 'email', 'contato'];
        $dominios = ['gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com.br'];

        return $prefixos[array_rand($prefixos)] . rand(100, 9999) . '@' . $dominios[array_rand($dominios)];
    }

    private function gerarTelefoneAleatorio(): string
    {
        $ddd = rand(11, 99);
        $numero = '9' . rand(10000000, 99999999);

        return $ddd . $numero;
    }

    private function gerarDocumentoAleatorio(): string
    {
        return sprintf('%011d', rand(10000000000, 99999999999));
    }

    private function gerarIpAleatorio(): string
    {
        return rand(1, 255) . '.' . rand(0, 255) . '.' . rand(0, 255) . '.' . rand(1, 255);
    }

    public function failed(\Throwable $exception): void
    {
        \Log::error('Job de processamento falhou definitivamente', [
            'notificacao_id' => $this->notificacaoId,
            'error' => $exception->getMessage(),
            'trace' => $exception->getTraceAsString()
        ]);

        try {
            $notificacao = Notificacao::find($this->notificacaoId);

            if ($notificacao) {
                $notificacao->update([
                    'status' => 'erro',
                ]);
            }
        } catch (\Exception $e) {
            \Log::error('Erro ao atualizar status da notificação após falha', [
                'notificacao_id' => $this->notificacaoId,
                'error' => $e->getMessage()
            ]);
        }
    }
}
