<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Database\Eloquent\Builder;

use App\Models\PaymentLink;
use App\Models\PaymentLinkConnexpayToken;

use App\Mail\PaymentLink as PaymentLinkMail;

use net\authorize\api\contract\v1 as AnetAPI;
use net\authorize\api\controller as AnetController;

class PaymentLinkController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index(Request $request)
    {
        $filter = json_decode($request->get('filter'));

        $get = PaymentLink::with([
            'user',
            'userUpdate',
            'status',
            'trace' => fn($q) => $q->orderBy('id', 'DESC'),
            'trace.status'
        ])
        ->where([
            ['id', 'LIKE', "%$filter->id%"],
            ['invoice', 'LIKE', "%$filter->invoice%"],
            ['amount', 'LIKE', "%$filter->amount%"]
        ]);

        if(isset($filter->user) && $filter->user) {
            $get = $get->whereHas('user', function($query) use ($filter) {
                $query->where('name', 'LIKE', '%' . $filter->user . '%');
            });
        }

        if(isset($filter->status) && $filter->status) {
            $get = $get->whereHas('status', function($query) use ($filter) {
                $query->where('name', 'LIKE', '%' . $filter->status . '%');
            });
        }

        $get = $get->orderBy('id', 'DESC')->paginate($request->get('rows'));

        return response()->json($get, 200);
            
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request)
    {
        try {
            DB::beginTransaction();

            $token = uniqid(base64_encode(Str::random(60)));

            $newPaymentLink = new PaymentLink;
            $newPaymentLink->id_status = 1; //ENVIADO AL CLIENTE
            $newPaymentLink->id_user = Auth::id();
            $newPaymentLink->id_user_update = Auth::id();
            $newPaymentLink->amount = $request->form['amount'];
            $newPaymentLink->description = isset($request->form['description']) ? $request->form['description'] : NULL;
            $newPaymentLink->token = $token;
            $newPaymentLink->expiration_token = date('Y-m-d H:i:s', strtotime('+ 2 hour ' . date('Y-m-d H:i:s')));
            $newPaymentLink->email = $request->form['email'];
            $newPaymentLink->email_name = strtoupper($request->form['name']);
            $newPaymentLink->email_lastname = strtoupper($request->form['lastname']);
            $newPaymentLink->invoice = $request->form['invoice'];
            $newPaymentLink->save();

            $lastConnexpayPaymentId = PaymentLink::max('id');

            $data = [$request->all(), $token];
            $to = $request->form['email'];
            $name = $request->form['name'] . ' ' . $request->form['lastname'];

            $email = Mail::to($to, $name);
            $email = $email->send(new PaymentLinkMail($data));

            DB::commit();
            return response()->json('Link de pago generado con éxito', 200);
        } catch(Exception $e){
            DB::rollback();
            return response()->json('[Error - PaymentLinkController@store]: ' . $e, 500);
        }

    }

    /**
     * Display the specified resource.
     */
    public function show(PaymentLink $paymentLink)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     */
    public function update(Request $request, PaymentLink $paymentLink)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     */
    public function destroy($id)
    {
        try {
            DB::beginTransaction();

            $update = PaymentLink::find($id);
            $update->id_status = 4; //Link cancelado
            $update->id_user_update = Auth::id();
            $update->save();

            DB::commit();
            return response()->json('Link de pago cancelado', 200);
        } catch (Exception $e) {
            DB::rollback();
            return response()->json('[Error - PaymentLinkController@destroy]: ' . $e, 500);
        }
    }

    public function getPaymentData($token) {
        $get = PaymentLink::where('token', $token)->first();
        if(!$get) return response()->json('Token inválido', 400);
        return response()->json($get, 200);
    }

    public function generateConnexpayToken() {
        try {
            PaymentLinkConnexpayToken::truncate();

            DB::beginTransaction();

            $methodHttp = 'POST';
            $endpoint = config('app.CONNEXPAY_S_URL') . '/api/v1/token';
            $headers = ['Content-Type' => 'application/x-www-form-urlencoded'];
            $options = [
                'form_params' => [
                    'grant_type' => 'password',
                    'username' => config('app.CONNEXPAY_USERNAME'),
                    'password' => config('app.CONNEXPAY_PASSWORD')
            ]];

            $client = new Client;
            $response = $client->request($methodHttp, $endpoint, $options, $headers);
            $response = json_decode($response->getBody()->getContents());
          
            $newAccessToken = new PaymentLinkConnexpayToken;
            $newAccessToken->token_type = $response->token_type;
            $newAccessToken->access_token = $response->access_token;
            $newAccessToken->expires_in = $response->expires_in;
            $newAccessToken->save();
            
            DB::commit();
        } catch(Exception $th) {
            DB::rollback();
            \Log::error('Error al generar token de autenticación connexpay: ' . $th);
            throw $th;
        }
    }

    // public function processPayment(Request $request) {
    //     try {
    //         $getPaymentData = PaymentLink::where('token', $request->token)->first();
    //         if(!$getPaymentData) return response()->json('Token Inválido', 400);

    //         $getAccessToken = PaymentLinkConnexpayToken::first();

    //         $methodHttp = 'POST';
    //         $endpoint = config('app.CONNEXPAY_S_URL') . '/api/v1/sales';
    //         $options = [
    //             'body' => json_encode([
    //                 "DeviceGuid" => config('app.CONNEXPAY_DEVICE'),
    //                 'Amount' => $getPaymentData->amount,
    //                 'OrderNumber' => $getPaymentData->invoice,
    //                 "SendReceipt" => true,
    //                 "RiskProcessingOnly" => false,
    //                 "ConnexPayTransaction" => [
    //                     "ExpectedPayments" => 0
    //                 ],
    //                  'RiskData' => [
    //                     'ProductType' => $getPaymentData->description,
    //                     'ProductDescription' => $getPaymentData->description,
    //                     'ProductItem' => $getPaymentData->invoice,
    //                     "ProductQuantity" => 1
    //                 ],
    //                 "Card" => [
    //                     "CardHolderName" => $request->form['cardName'],
    //                     "CardNumber" => $request->form['cardNumber'],
    //                     "CVV2" => $request->form['cardCvv'],
    //                     "ExpirationDate" => $request->form['cardExpYear'] . $request->form['cardExpMonth'],
    //                     "Customer" => [
    //                         "FirstName" => substr($request->form['cardName'], 0, 30),
    //                         "LastName" => substr($request->form['cardName'], 0, 30),
    //                         "Email" => $request->form['cardEmail'],
    //                         "Address1" => $request->form['cardAddress1'],
    //                         "Address2" => $request->form['cardAddress1'] ? $request->form['cardAddress1'] : '',
    //                         "Zip" => $request->form['cardZip']
    //                     ]
    //                 ]
    //             ]),

    //             'headers' => [
    //                 'Content-Type' => 'application/json',
    //                 'Accept' => '*/*',
    //                 'Authorization' => 'Bearer ' . $getAccessToken->access_token,
    //             ]
    //         ];

    //         $client = new Client;
    //         $response = $client->request($methodHttp, $endpoint, $options);
    //         $response = json_decode($response->getBody()->getContents());

    //         $updatePayment = PaymentLink::find($getPaymentData->id);
    //         $updatePayment->connexpay_guid = $response->guid;
    //         $updatePayment->connexpay_status = $response->status;
    //         $updatePayment->connexpay_type = $response->type;
    //         $updatePayment->connexpay_batch_status = $response->batchStatus;
    //         $updatePayment->connexpay_time_stamp = $response->timeStamp;
    //         $updatePayment->connexpay_device_guid = $response->deviceGuid;
    //         $updatePayment->connexpay_amount = $response->amount;
    //         $updatePayment->connexpay_activated = $response->activated;
    //         $updatePayment->connexpay_tender_type = $response->tenderType;
    //         $updatePayment->connexpay_effective_amount = $response->effectiveAmount;
    //         $updatePayment->connexpay_risk_response_transaction_id = isset($response->riskResponse) ? $response->riskResponse->transactionId : null;
    //         $updatePayment->connexpay_risk_response_response = isset($response->riskResponse) ? $response->riskResponse->response : null;
    //         $updatePayment->connexpay_risk_response_reason = isset($response->riskResponse) ? $response->riskResponse->reason : null;
    //         $updatePayment->connexpay_risk_response_score = isset($response->riskResponse) ? $response->riskResponse->score : null;
    //         $updatePayment->connexpay_risk_response_omniscore = isset($response->riskResponse) ? $response->riskResponse->omniscore : null;
    //         $updatePayment->connexpay_order_number = $response->orderNumber;
    //         $updatePayment->connexpay_card_data_source = $response->cardDataSource;
    //         $updatePayment->connexpay_batch_guid = $response->batchGuid;
    //         $updatePayment->connexpay_connexpay_transaction_guid = isset($response->connexPayTransaction) ? $response->connexPayTransaction->guid : null;
    //         $updatePayment->connexpay_connexpay_transaction_expected_payments = isset($response->connexPayTransaction) ? $response->connexPayTransaction->expectedPayments : null;
    //         $updatePayment->connexpay_connexpay_transaction_incoming_trans_code = isset($response->connexPayTransaction) ? $response->connexPayTransaction->incomingTransCode : null;
    //         $updatePayment->connexpay_processor_status_code = $response->processorStatusCode;
    //         $updatePayment->connexpay_processor_response_message = $response->processorResponseMessage;
    //         $updatePayment->connexpay_was_processed = $response->wasProcessed;
    //         $updatePayment->connexpay_auth_code = isset($response->authCode) ? $response->authCode : null;
    //         $updatePayment->connexpay_ref_number = isset($response->refNumber) ? $response->refNumber : null;
    //         $updatePayment->connexpay_customer_receipt = isset($response->refNumber) ? $response->customerReceipt : null;
    //         $updatePayment->connexpay_generated_by = $response->generatedBy;
    //         $updatePayment->connexpay_card_card_holder_name = $response->card->cardHolderName;
    //         $updatePayment->connexpay_card_card_type = $response->card->cardType;
    //         $updatePayment->connexpay_address_verification_code = isset($response->addressVerificationCode) ? $response->addressVerificationCode : null;
    //         $updatePayment->connexpay_address_verification_result = $response->addressVerificationResult;
    //         $updatePayment->connexpay_cvv_verification_code = isset($response->cvvVerificationCode) ? $response->cvvVerificationCode : null;
    //         $updatePayment->connexpay_cvv_verification_result = $response->cvvVerificationResult;
    //         $updatePayment->connexpay_cavv_response_code = isset($response->cavvResponseCode) ? $response->cavvResponseCode : null;
    //         $updatePayment->connexpay_wallet_provider = $response->walletProvider;
    //         $updatePayment->connexpay_is_from_issue_lite = $response->isFromIssueLite;
    //         $updatePayment->connexpay_remaining_amount = $response->remainingAmount;

    //         if($response->status === "Transaction - Approved") {
    //             $updatePayment->token = null;
    //             $updatePayment->expiration_token = null;
    //             $updatePayment->id_status = 2; //PAGO REALIZADO CON EXITO;
    //         } else {
    //             $updatePayment->id_status = 5; //TRANSACCION FALLIDA
    //         }

    //         $updatePayment->save();

    //         if($response->status === "Transaction - Approved") return response()->json('¡Payment Processed!', 200);
    //         else return response()->json($response->status, 400);
    //     } catch (Exception $th) {
    //         dd($th);
    //     }
    // }

    public function processPayment(Request $request) {
        $paymentData = PaymentLink::where('token', $request->token)->first();
        if(!$paymentData) return response()->json('Token Inválido', 400);

        $transactionResponse = $this->chargeCreditCard($paymentData, (object) $request->all());

        try {
            DB::beginTransaction();

            $paymentData->authorize_ref_id = $transactionResponse->getRefId();
            $paymentData->authorize_message_result_code = $transactionResponse->getMessages()->getResultCode();
            $paymentData->authorize_message_code = $transactionResponse->getMessages()->getMessage()[0]->getCode();
            $paymentData->authorize_message_text = $transactionResponse->getMessages()->getMessage()[0]->getText();
            $paymentData->authorize_transaction_response_code = $transactionResponse->getTransactionResponse()->getResponseCode();
            $paymentData->authorize_transaction_auth_code = $transactionResponse->getTransactionResponse()->getAuthCode();
            $paymentData->authorize_transaction_avs_result_code = $transactionResponse->getTransactionResponse()->getAvsResultCode();
            $paymentData->authorize_transaction_cvv_result_code = $transactionResponse->getTransactionResponse()->getCvvResultCode();
            $paymentData->authorize_transaction_cavv_result_code = $transactionResponse->getTransactionResponse()->getCavvResultCode();
            $paymentData->authorize_transaction_trans_id = $transactionResponse->getTransactionResponse()->getTransId();
            $paymentData->authorize_transaction_ref_trans_id = $transactionResponse->getTransactionResponse()->getRefTransId();
            $paymentData->authorize_transaction_account_type = $transactionResponse->getTransactionResponse()->getAccountType();
            $paymentData->authorize_transaction_message_code = $transactionResponse->getTransactionResponse()->getMessages() ? $transactionResponse->getTransactionResponse()->getMessages()[0]->getCode() : null;
            $paymentData->authorize_transaction_message_description = $transactionResponse->getTransactionResponse()->getMessages() ? $transactionResponse->getTransactionResponse()->getMessages()[0]->getDescription() : null;
            $paymentData->authorize_transaction_error_code = $transactionResponse->getTransactionResponse()->getErrors() ? $transactionResponse->getTransactionResponse()->getErrors()[0]->getErrorCode() : null;
            $paymentData->authorize_transaction_error_text = $transactionResponse->getTransactionResponse()->getErrors() ? $transactionResponse->getTransactionResponse()->getErrors()[0]->getErrorText() : null;

            switch ($transactionResponse->getTransactionResponse()->getResponseCode()) {
                case '1':
                    $paymentData->token = null;
                    $paymentData->expiration_token = null;
                    $paymentData->id_status = 2;
                    
                    $response = response()->json('¡Pago realizado con éxito!', 200);
                    break;
                
                default:
                    $paymentData->id_status = 5;
                    $response = response()->json(['La transacción no tuvo éxito', $transactionResponse->getTransactionResponse()->getErrors()[0]->getErrorText()], 400);
                    break;
            }
    
            $paymentData->save();

            DB::commit();
            return $response;
        } catch (Exception $e) {
            DB::rollback();
            return response()->json('[Error - PaymentLinkController@processPayment]: ' . $e, 500);
        }
    }

    public function chargeCreditCard($paymentData, $data)
    {
        $refId = 'ref' . time();
        $merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
        $merchantAuthentication->setName(config('app.AUTHORIZE_API_LOGIN_ID'));
        $merchantAuthentication->setTransactionKey(config('app.AUTHORIZE_TRANSACTION_KEY'));

        $creditCard = new AnetAPI\CreditCardType();
        $creditCard->setCardNumber($data->form['cardNumber']);
        $creditCard->setExpirationDate("20" . $data->form['cardExpYear'] . "-" . $data->form['cardExpMonth']);
        $creditCard->setCardCode($data->form['cardCvv']);

        $paymentOne = new AnetAPI\PaymentType();
        $paymentOne->setCreditCard($creditCard);

        $order = new AnetAPI\OrderType();
        $order->setInvoiceNumber($paymentData->invoce);
        $order->setDescription($paymentData->description);

        \Log::info(json_encode($data->form));
        $customerAddress = new AnetAPI\CustomerAddressType();
        $customerAddress->setFirstName($data->form['cardName']);
        $customerAddress->setAddress($data->form['cardAddress1'] . " " . $data->form['cardAddress2']);
        $customerAddress->setZip($data->form['cardZip']);
        $customerAddress->setCountry($data->form['cardCountry']);

        $customerData = new AnetAPI\CustomerDataType();
        $customerData->setType("individual");
        $customerData->setId("0");
        $customerData->setEmail($data->form['cardEmail']);

        $duplicateWindowSetting = new AnetAPI\SettingType();
        $duplicateWindowSetting->setSettingName("duplicateWindow");
        $duplicateWindowSetting->setSettingValue("60");

        $transactionRequestType = new AnetAPI\TransactionRequestType();
        $transactionRequestType->setTransactionType("authCaptureTransaction");
        $transactionRequestType->setAmount($paymentData->amount);
        $transactionRequestType->setOrder($order);
        $transactionRequestType->setPayment($paymentOne);
        $transactionRequestType->setBillTo($customerAddress);
        $transactionRequestType->setCustomer($customerData);
        $transactionRequestType->addToTransactionSettings($duplicateWindowSetting);

        $request = new AnetAPI\CreateTransactionRequest();
        $request->setMerchantAuthentication($merchantAuthentication);
        $request->setRefId($refId);
        $request->setTransactionRequest($transactionRequestType);

        $controller = new AnetController\CreateTransactionController($request);
        // $response = $controller->executeWithApiResponse(
        //     config('app.APP_ENV') === 'production' 
        //         ? \net\authorize\api\constants\ANetEnvironment::PRODUCTION
        //         : \net\authorize\api\constants\ANetEnvironment::SANDBOX
        //     );
        $response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION);

        return $response;
    }
}
