<?php

namespace App\Http\Controllers\Api\Platform;

use App\Exceptions\CouponExpiredException;
use App\Exceptions\DoublePurchaseException;
use App\Exceptions\InsufficientWalletBalanceException;
use App\Exceptions\InvalidBookPurchaseException;
use App\Exceptions\InvalidBusinessCoupon;
use App\Exceptions\InvalidCouponAmountToSpendException;
use App\Exceptions\InvalidOrderCancellationAttempt;
use App\Exceptions\InvalidOrderDeliveryConfirmationAttempt;
use App\Exceptions\InvalidOrderDeliveryDate;
use App\Exceptions\InvalidOrderPaymentAttempt;
use App\Exceptions\InvalidOrderValidationAttempt;
use App\Exceptions\WalletBlockedException;
use App\Exceptions\WalletRemainingPinAttemptsException;
use App\Http\Controllers\Controller;
use App\Models\Order;
use App\RequestRules\Api\OrderRequestRules;
use Exception;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Routing\ResponseFactory;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class OrderController extends Controller
{
   /**
    * Get order details
    *
    * @param int $id
    * @return Response|Application|ResponseFactory
    */
   public function orderDetails(int $id): Response|Application|ResponseFactory
   {
      /** @var Order $order */
      $order = Order::findById($id);

      if ($order === null) {
         return api_response(120, __('errors.unknown_order'));
      }

      return api_response(100, "Okay", $order->computeOrderDetails());
   }

   /**
    * Add order
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function add(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::add());
      $data = $request->all();
      try {
         Order::addOrder($data);

         return api_response(100, "Okay");
      } catch (\Exception $exception) {
         log_debug($exception, "OrderController::add");
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Get validated orders
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function list(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::orderList());

      $data = $request->all();

      try {
         $finalData = Order::computeOrderDetailFromPaginator(
            Order::orderList(
               $data["request_type"],
               $data["page"]
            )
         );

         return api_response(100, "Okay", $finalData);
      } catch (\Exception $exception) {
         log_debug($exception, "OrderController::list");
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Validate pending order
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function validateOrder(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::validate());
      $data = $request->all();
      try {
         $orderValidation = Order::validateOrder($data);

         return api_response(
            100,
            "Okay",
            $orderValidation->computeOrderDetails()
         );
      } catch (Exception $exception) {
         log_debug($exception, "OrderController::validateOrder");
         if ($exception instanceof InvalidOrderDeliveryDate || $exception instanceof InvalidOrderValidationAttempt) {
            return api_response(120, $exception->getMessage());
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function pay(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::pay());
      $data = $request->all();

      try {
         /** @var ?Order $paymentResult */
         $paymentResult = Order::orderPayment($data);

         return api_response(100, "Okay", $paymentResult->computeOrderDetails());
      } catch (\Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::pay');
         if ($exception instanceof InvalidOrderPaymentAttempt) {
            return api_response(120, $exception->getMessage());
         }

         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Order delivery confirmation
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function confirmDelivery(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::confirmDelivery());

      try {
         /** @var ?Order $deliveryConfirmation */
         $deliveryConfirmation = Order::confirmDelivery(
            $request->get("order_id")
         );

         return api_response(
            100,
            "Okay",
            $deliveryConfirmation->computeOrderDetails(),
         );
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::confirmDelivery');
         if ($exception instanceof InvalidOrderDeliveryConfirmationAttempt) {
            return api_response(120, $exception->getMessage());
         }
      }
      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Cancel order
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function cancel(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::cancelOrder());
      $data = $request->all();

      try {
         $orderCancellation = Order::cancelOrder($data);

         return api_response(
            100,
            "Okay",
            $orderCancellation->computeOrderDetails()
         );
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::cancel');

         if ($exception instanceof InvalidOrderCancellationAttempt) {
            return api_response(120, $exception->getMessage());
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy movie
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buyMovie(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::movie());
      $data = $request->all();

      try {
         $buyMovie = Order::buyMovie($data);
         return api_response(100, "Okay", $buyMovie->computeOrderDetails());
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buyMovie');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy series
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buySeries(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::series());
      $data = $request->all();
      try {
         $buyMovie = Order::buySeries($data);
         return api_response(100, "Okay", $buyMovie->computeOrderDetails());

      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buySeries');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy soft copy book
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buyBook(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::books());
      $data = $request->all();
      try {
         $book = Order::buyBook($data);
         return api_response(100, "Okay", $book->computeOrderDetails());
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buyBook');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy music
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buyMusic(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::music());
      $data = $request->all();
      try {
         $music = Order::buyMusic($data);
         return api_response(100, "Okay", $music->computeOrderDetails());
      } catch (Exception$exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buyMusic');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy music album
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buyMusicAlbum(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::musicAlbum());
      $data = $request->all();
      try {
         $musicAlbum = Order::buyMusicAlbum($data);
         return api_response(100, "Okay", $musicAlbum->computeOrderDetails());
      } catch (Exception$exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buyAlbum');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Buy game
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function buyGame(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::game());
      $data = $request->all();
      try {
         $game = Order::buyGame($data);
         return api_response(100, "Okay", $game->computeOrderDetails());
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::buyGame');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Get auth user purchases
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function purchases(Request $request): Response|Application|ResponseFactory
   {
      try {
         $finalData = Order::computeOrderDetailFromPaginator(
            Order::purchases(
               $request->get("query"),
               $request->get("page")
            )
         );

         return api_response(100, "Okay", $finalData);
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::purchases');
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Get business purchases
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function sales(Request $request): Response|Application|ResponseFactory
   {
      try {
         $finalData = Order::computeOrderDetailFromPaginator(
            Order::sales(
               $request->get("query"),
               $request->get("page")
            )
         );

         return api_response(100, "Okay", $finalData);
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::sales');
      }

      return api_response(120, __("errors.unknown_error"));
   }

   /**
    * Compute coupon amount
    *
    * @param Request $request
    * @return Application|ResponseFactory|Response
    */
   public function computeCouponAmount(Request $request): Response|Application|ResponseFactory
   {
      $request->validate(OrderRequestRules::computeCouponAmount());
      $data = $request->all();

      try {
         $result = Order::computeCouponAmount($data);
         if ($result != null) {
            return api_response(100, "Okay", $result);
         }
      } catch (Exception $exception) {
         log_debug(exception: $exception, prefix: 'OrderController::computeCouponAmount');
         $serviceExceptions = self::returnServiceExceptions($exception);
         if ($serviceExceptions != null) {
            return $serviceExceptions;
         }
      }

      return api_response(120, __("errors.unknown_error"));
   }


   //Controller utilities

   /**
    * Return exception
    *
    * @param Exception $exception
    * @return Response|Application|ResponseFactory|null
    */
   protected static function returnServiceExceptions(Exception $exception): Response|Application|ResponseFactory|null
   {
      if ($exception instanceof WalletBlockedException || $exception instanceof WalletRemainingPinAttemptsException || $exception instanceof InsufficientWalletBalanceException || $exception instanceof InvalidBookPurchaseException || $exception instanceof DoublePurchaseException || $exception instanceof InvalidCouponAmountToSpendException || $exception instanceof CouponExpiredException || $exception instanceof InvalidBusinessCoupon) {
         return api_response(120, $exception->getMessage());
      }

      return null;
   }

}
