<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Http\Resources\CartResource;
use App\Models\PersonalAccessToken;
use Illuminate\Http\Request;
use App\Models\Cart;
use App\Models\Currency;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class CartController extends Controller
{
    public function getCart()
    {
        $currency = Currency::latest()->first();
        // add currency to cache
        cache()->put('currency', $currency, now()->addMinutes(60));

        $bearer = $this->getBearerToken();

        if ($bearer) {
            [$personalAccessTokenId, $token] = explode('|', $bearer, 2);
            $personalAccessToken = PersonalAccessToken::find($personalAccessTokenId);

            if ($personalAccessToken && hash_equals($personalAccessToken->token, hash('sha256', $token))) {
                // Authenticated
                $cart = Cart::with('product')->whereHas('product', function ($product) {
                    $product->where('child_id', null);
                })->where('user_id', $personalAccessToken->tokenable_id)->orderBy('id', 'desc')->get();
            }
        } else {
            // Check for local token in request headers
            $localToken = $this->getLocalToken();
            if ($localToken) {
                $cart = Cart::with('product')->whereHas('product', function ($product) {
                    $product->where('child_id', null);
                })->where('token', $localToken)->get();
            }
        }

        $price_solutions = 0;
        $discount_solutions = 0;
        $total_solutions = 0;

        $price = 0;
        $discount = 0;
        $total = 0;

        $getReadySolutionProductsCart = collect([]);
        $getSingleProductsCart = collect([]);

        if (isset($cart)) {
            $getReadySolutionProductsCart = $cart->filter(function ($cart) {
                return $cart->product->is_ready_solution == true;
            });

            $ready_solutions_count = 0;
            $price_solutions = $getReadySolutionProductsCart->filter(function ($cart) {
                return $cart->product->count >= $cart->count;
            })->map(function ($cart) use (&$ready_solutions_count) {
                $ready_solutions_count += $cart->count;
                $cart->product->price_total = !empty($cart->product->price_discount) ? $cart->product->price_discount * $cart->count : $cart->product->price * $cart->count;
                $cart->product->price_discount_total = !empty($cart->product->price_discount) ? ($cart->product->price - $cart->product->price_discount) * $cart->count : 0;
                $cart->product->price_products = $cart->product->price * $cart->count;
                return $cart;
            });

            $discount_solutions = $price_solutions->sum('product.price_discount_total');
            $total_solutions = $price_solutions->sum('product.price_total');
            $price_solutions = $price_solutions->sum('product.price_products');

            $single_products_count = 0;
            $getSingleProductsCart = collect($cart)->filter(function ($cart) {
                return $cart->product->is_ready_solution == false;
            });

            $price = $getSingleProductsCart->filter(function ($cart) {
                return $cart->product->count >= $cart->count;
            })->map(function ($cart) use (&$single_products_count) {
                $single_products_count += $cart->count;
                $cart->product->price_total = !empty($cart->product->price_discount) ? $cart->product->price_discount * $cart->count : $cart->product->price * $cart->count;
                $cart->product->price_discount_total = !empty($cart->product->price_discount) ? ($cart->product->price - $cart->product->price_discount) * $cart->count : 0;
                $cart->product->price_products = $cart->product->price * $cart->count;
                return $cart;
            });

            $discount = $price->sum('product.price_discount_total');
            $total = $price->sum('product.price_total');
            $price = $price->sum('product.price_products');
        }

        // get currency from cache
        $currency = cache()->get('currency');

        return [
            'data' => [
                'ready_solutions' => [
                    'products' => CartResource::collection($getReadySolutionProductsCart),
                    'price' => ceiling($price_solutions * $currency->dollar, 100),
                    'discount' => ceiling($discount_solutions * $currency->dollar, 100),
                    'total' => ceiling($total_solutions * $currency->dollar, 100),
                ],
                'ready_solutions_count' => $ready_solutions_count,
                'single_products' => [
                    'products' => CartResource::collection($getSingleProductsCart),
                    'price' => ceiling($price * $currency->dollar, 100),
                    'discount' => ceiling($discount * $currency->dollar, 100),
                    'total' => ceiling($total * $currency->dollar, 100),
                ],
                'single_products_count' => $single_products_count
            ]
        ];
    }

    private function getUser()
    {
        $bearer = $this->getBearerToken();

        $type = null;
        $user = null;
        $token = null;

        if ($bearer) {
            [$personalAccessTokenId, $token] = explode('|', $bearer, 2);
            $personalAccessToken = PersonalAccessToken::find($personalAccessTokenId);

            if ($personalAccessToken && hash_equals($personalAccessToken->token, hash('sha256', $token))) {
                // Authenticated
                $type = 'user';
                $user = User::findOrFail($personalAccessToken->tokenable_id);
                $token = null;
            }
        } else {
            // Check for local token in request headers
            $localToken = $this->getLocalToken();
            if ($localToken) {
                $type = 'token';
                $user = null;
                $token = $localToken;
            }
        }

        return [
            'type' => $type,
            'user' => $user,
            'token' => $token
        ];
    }

    public function addToCart(Request $request)
    {
        try {
            // Validate the request
            $request->validate([
                'product_id' => 'required|exists:products,id',
            ]);

            // Retrieve the bearer token from the request
            $bearer = $this->getBearerToken();

            if ($bearer) {
                [$personalAccessTokenId, $token] = explode('|', $bearer, 2);
                $personalAccessToken = PersonalAccessToken::find($personalAccessTokenId);

                if ($personalAccessToken && hash_equals($personalAccessToken->token, hash('sha256', $token))) {
                    // Authenticated
                    $user = User::find($personalAccessToken->tokenable_id);

                    if ($user) {
                        $this->updateOrCreateCart($user, $request->product_id);
                        return response()->json(['status' => true, 'data' => null]);
                    }
                }
            }

            // Check for local token in request headers
            $localToken = $this->getLocalToken();
            if (!$localToken) {
                return response()->json(['message' => 'Unauthorized'], 401);
            }

            $this->updateOrCreateCartByToken($localToken, $request->product_id);
        } catch (\Throwable $e) {
            return response()->json([
                'message' => $e->getMessage()
            ], 403);
        }

        return response()->json(['status' => true, 'data' => null]);
    }

    public function add(Cart $cart)
    {
        $user =  $this->getUser();

        try {
            DB::beginTransaction();
            if (empty($user['type']))
                abort(403);

            if ($user['type'] == 'token') {
                if ($cart->token != $user['token'])
                    abort(403);
            } else if ($user['type'] == 'user') {
                if ($cart->user_id != $user['user']->id)
                    abort(403);
            }

            $cart->update([
                'count' => $cart->count + 1
            ]);
        } catch (\Throwable $e) {
            report($e);
            DB::rollBack();
            return response()->json([
                'message' => trans('errors.try_again')
            ])->setStatusCode(403);
        }

        DB::commit();

        return $this->getCart();
    }

    public function decrease(Cart $cart)
    {
        $user =  $this->getUser();

        try {
            DB::beginTransaction();
            if (empty($user['type']))
                abort(403);

            if ($user['type'] == 'token') {
                if ($cart->token != $user['token'])
                    abort(403);
            } else if ($user['type'] == 'user') {
                if ($cart->user_id != $user['user']->id)
                    abort(403);
            }

            if ($cart->count <= 1) {
                $cart->delete();
            } else {
                $cart->update([
                    'count' => $cart->count - 1
                ]);
            }
        } catch (\Throwable $e) {
            report($e);
            DB::rollBack();
            return response()->json([
                'message' => trans('errors.try_again')
            ])->setStatusCode(403);
        }

        DB::commit();

        return $this->getCart();
    }

    public function delete($cartId)
    {
        try {
            DB::beginTransaction();
            Cart::find($cartId)->delete();
        } catch (\Throwable $e) {
            report($e);
            DB::rollBack();
            return response()->json([
                'message' => trans('errors.try_again')
            ])->setStatusCode(403);
        }

        DB::commit();

        return $this->getCart();
    }

    protected function updateOrCreateCart($user, $productId)
    {
        $cart = $user->cart()->where('product_id', $productId)->first();

        try {
            if ($cart) {
                // Update the existing cart item
                Cart::where('id', $cart->id)->update([
                    'count' => $cart->count + 1
                ]);
            } else {
                // Create a new cart item
                $user->cart()->create([
                    'product_id' => $productId,
                    'count' => 1,
                ]);
            }
        } catch (\Throwable $e) {
            report($e);
            return response()->json([
                'message' => trans('errors.try_again')
            ])->setStatusCode(403);
        }
    }

    protected function updateOrCreateCartByToken($localToken, $productId)
    {
        $cart = Cart::where('token', $localToken)
            ->where('product_id', $productId)
            ->first();

        if ($cart) {
            // Update the existing cart item
            Cart::where('id', $cart->id)->update([
                'count' => $cart->count + 1
            ]);
        } else {
            // Create a new cart item
            Cart::create([
                'token' => $localToken,
                'product_id' => $productId,
                'count' => 1,
            ]);
        }
    }

    private function getLocalToken()
    {
        return request()->header('X-Application-Token');
    }

    private function getBearerToken()
    {
        return request()->bearerToken();
    }
}
