<?php


namespace App\Repositories;


use App\Box;
use App\Notifications\NewUserSubscriptionNotification;
use App\BoxOrder;
use App\Payment;
use App\PaymentType;
use App\Role;
use App\StripeUser;
use App\UserSubscription;
use App\Pricing;
use App\User;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

class UserSubscriptionRepository implements RepositoryInterface {

    public const SUBSCRIPTION_STATUS_ANY = 1;
    public const SUBSCRIPTION_STATUS_ACTIVE = 2;
    public const SUBSCRIPTION_STATUS_NEARLY_DUE = 3; //A PUNTO DE CADUCAR
    public const SUBSCRIPTION_STATUS_EXTENSION = 4; //PRORROGA
    public const SUBSCRIPTION_STATUS_DUE = 5; //inactiva

    public static function all() {
        return UserSubscription::all();
    }

    public static function create(array $data)
    {
        // Begin Transaction
        DB::beginTransaction();

        try {
            // Mirem si podem crear una subscripció amb el rol escollit depenent de la capacitat de la company
            if (!CompanyRepository::find(BoxRepository::find($data['box_id'])->company_id)->canCreate($data['role_id'])) {
                $r = RoleRepository::find($data['role_id'])->name;
                if ($r == 'Super Admin') $m = "No puedes crear un Super Admin";
                else $m = "No tienes mas capacidad para crear un nuevo $r, prueba a mejorar tu tarifa";
                throw new \Exception($m);
            }

            $pricing = Pricing::find($data['pricing_id']);

            // La tarifa i el box que ho esta generant tenen relació
            if (count($pricing->boxes->where('id', $data['box_id'])) == 0) {
                throw new \Exception("No puedes generar una subscripcion de una tarifa que no es de tu box");
            }

            $s = [
                "pricing_id" => $pricing->id,
                "user_id" => $data['user_id'],
                "role_id" => $data['role_id'],
                "start_date" => null,
                "end_date" => isset($data["start_date"]) ? $data["start_date"] : null, //per quan s'estigui cambiant a una nova
                "price" => $pricing->price,
                "wod_counter" => $pricing->wod_counter,
                "open_counter" => $pricing->open_counter,
                "total_counter" => $pricing->total_counter,
                "renew" => $data['renew'] // Més tard, quan pagui, mirarem si es posible que faci la renovació automatica
            ];
            $sub = UserSubscription::create($s);

            // Generem la linia del box order
            $vat = Box::find($data['box_id'])->tax_prcnt_box_order;
            $bol = [
                "user_subscription_id" => $sub->id,
                "quantity" => 1,
                "price" => $pricing->price ? $pricing->price : 0,
                "vat" => $vat,
                "discount_id" => (isset($data['discount_id']) && !is_null($data["discount_id"])) ? $data['discount_id'] : null
            ];
            // Generem box order
            $bo = [
                "box_id" => $data['box_id'],
                "user_id" => $data['user_id'],
                "lines" => [$bol],
                "discount_id" => null
            ];

            //afegim matricula
            if(isset($data["matricula"]) && !is_null($data["matricula"])) {
                $matricula_line = [
                    "quantity" => 1,
                    "price" => $data["matricula"],
                    "vat" => $vat
                ];

                array_push($bo["lines"], $matricula_line);
            }

            $boxOrder = BoxOrderRepository::create($bo);

            // Notificar nova subscripció
            $user = $sub->user;
            $company = $sub->company;
            $role = $sub->role;

            $user->notify((new NewUserSubscriptionNotification($user, $company, $pricing, $role, $sub))->delay(Carbon::now()->addSeconds(5)));
            $company->notify((new NewUserSubscriptionNotification($user, $company, $pricing, $role, $sub))->delay(Carbon::now()->addSeconds(5)));

            DB::commit();
        } catch (\Exception $e) {
            DB::rollback();
            throw $e;
        }
        return $sub;
    }

    public static function update(array $data, $id) {
        $s = UserSubscription::find($id);
        $s->update($data);
        return $s;
    }

    public static function delete($id){
        return UserSubscription::destroy($id);
    }

    public static function find($id){
        return UserSubscription::with("user", "box_orders")->where("id", $id)->first();
    }

    public static function renew($id, $data){
        DB::beginTransaction();

        try {
            $pricing = Pricing::find($data['pricing_id']);

            // La tarifa i el box que ho esta generant tenen relació
            if (count($pricing->boxes->where('id', $data['box_id'])) == 0) {
                return "No puedes generar una subscripcion de una tarifa que no es de tu box";
            }

            // A traves del ultim end_date, sumem el temps que s'allarga
            $user_subscription = UserSubscriptionRepository::find($id);
            /*$end_date_subscription = null;
            if ( $pricing->days ) {
                $months = 0;
                $days = 0;

                if ( $pricing->days >= 30 ) $months = (float)number_format($pricing->days/30, 0, '', '');
                $days = $pricing->days ? $pricing->days - $months*30 : 0;

                $end_date_subscription = Carbon::create($user_subscription->end_date->format('Y-m-d'))->addMonth($months)->addDays($days)->format('Y-m-d');
            }*/

            if ( $pricing->id != $user_subscription->pricing_id ) { // Vol un cavi de tarifa
                if ( isset($data['override']) && $data["override"]==true  ) { // El vol ara el canvi
                    // Finalitzem l'actual
                    UserSubscriptionRepository::update(["end_date" => Carbon::now()->addMinutes(-1)->format('Y-m-d')],$user_subscription->id);
                    //finalitzem ja la suscripcio d'stripe
                    if($user_subscription->stripe_id!=null && $user_subscription->renew) {
                        $user_subscription->cancelNow();
                    }
                }
                else {
                    //cancelem la suscripcio d'stripe
                    if($user_subscription->stripe_id!=null) {
                        $user_subscription->cancel();
                    }
                    else {
                        $user_subscription->ends_at = $user_subscription->end_date;
                        $user_subscription->save();
                    }
                }
                $s = [
                    "pricing_id" => $pricing->id,
                    "user_id" => $user_subscription->user_id,
                    "role_id" => $user_subscription->role_id,
                    "start_date" => null,
                    "end_date" => $data["override"]==true ? null : $user_subscription->end_date,
                    "price" => $pricing->price,
                    "wod_counter" => $pricing->wod_counter,
                    "open_counter" => $pricing->open_counter,
                    "total_counter" => $pricing->total_counter,
                    "renew" => $data['renew']
                ];

                $sub = UserSubscription::create($s);
                $msg = "Vol un canvi de tarifa";
            } else {
                if($user_subscription->renew==false && isset($data["renew"]) && $data["renew"]==true) { //si esta passant de renovacio manual a renovacio automatica
                    $user_subscription->renew = true;
                    $user_subscription->save();
                }
                $sub = $user_subscription;
                $msg = "No vol un canvi de tarifa";
            }

            //Create box order
            $boxOrder = UserSubscriptionRepository::createUserSubscriptionBoxOrder($sub->id, $user_subscription->user_id, $data['box_id'], $pricing, $data['discount_id']);

            // Notificar nova subscripció
            $user = $sub->user;
            $company = $sub->company;
            $role = $sub->role;
            $user->notify((new NewUserSubscriptionNotification($user, $company, $pricing, $role, $sub))->delay(Carbon::now()->addSeconds(5)));
            $company->notify((new NewUserSubscriptionNotification($user, $company, $pricing, $role, $sub))->delay(Carbon::now()->addSeconds(5)));

            DB::commit();
            return [$msg,$sub,$boxOrder];
        } catch (\Exception $e) {
            DB::rollback();
            throw new \Exception($e->getMessage());
        }
    }

    public static function changePricing($id, $data) {
        return "Canvi de tarifa";
    }

    /**
     * RELACIONALS
     */

    public static function payments($id){
        return UserSubscriptionRepository::find($id)->payments;
    }

    public static function company($id) {
        return UserSubscriptionRepository::find($id)->company;
    }

    public static function boxOrders($id) {
        return UserSubscriptionRepository::find($id)->box_orders;
    }

    /**
     * ALTRES FUNCIONS
     */

    /**
     * AQUEST METODE NOMES ES POT FER SERVIR PER DONAR D'ALTA EL PRIMER COMPANY ADMIN
     */
    public static function createCompanyFirstAdminUserSubscription($company_id, $user_id, $box_id) {
        return UserSubscription::create([
            "pricing_id" => Pricing::whereRaw("price is null and company_id = '$company_id'")->first()->id, //s'ha creat automaticament al crear l'empresa
            "user_id" => $user_id,
            "role_id" => Role::where("slug", "company_admin")->first()->id,
            "start_date" => Carbon::now(),
            "end_date" => null,
            "box_id" => $box_id,
            "price" => 0,
            "wod_counter" => null,
            "open_counter" => null,
            "total_counter" => null,
            "renew" => false
        ]);
    }

    public static function updateAfterPay($id, $payment_type_id, $start_date = null) {
        $user_subscription = UserSubscriptionRepository::find($id);
        $start_date = $user_subscription->end_date ?
                        $user_subscription->end_date :
                        (($start_date==null) ?
                            Carbon::now() :
                            $start_date);
        $pricing = PricingRepository::find($user_subscription->pricing_id);
        $end_date_subscription = null;

        if ( $pricing->days ) { //si no es qe es un bono
            if($user_subscription->start_date != null) {
                //estem ampliant la suscripció perque encara no ha començat
                $start_date = $user_subscription->start_date;
                $end_date_subscription = ($user_subscription->end_date ? $user_subscription->end_date : $start_date)->copy()->addDays($pricing->days);
            }
            else {
                //estem fentne una de nova
                $end_date_subscription = $start_date->copy()->addDays($pricing->days);
            }
        }

        $auto = PaymentTypeRepository::find($payment_type_id)->auto;

        return UserSubscriptionRepository::update(
            [
                "start_date"    =>  $start_date,
                "end_date"      =>  $end_date_subscription,
                "renew"         =>  $user_subscription->renew && $auto ? true : false // Si va escollir renovar, mirem si el metode de pagament realment te activa aquesta opció
            ],
            $id
        );
    }

    public static function payUserSubscription($id,$payment_type_slug,$box_id = null,$start_date = null, bool $fromWebhook = false)
    {
        DB::beginTransaction();
        try {
            $us_boxes = UserSubscriptionRepository::find($id)->boxes();

            if ($box_id!=null && count($us_boxes->where('id', $box_id)) == 0) {
                throw new \Exception("No puedes generar una subscripcion de una tarifa que no es de tu box");
            }

            $payment_type = PaymentTypeRepository::findBySlug($payment_type_slug);
            $us = self::updateAfterPay($id, $payment_type->id, $start_date);

            $payment = UserSubscriptionRepository::paymentToPay($id);
            if($payment==null) throw new \Exception("No se ha encontrado un pago a pagar para esta suscripción.");

            //seccio stripe
            if($payment_type_slug=="stripe" && $payment_type->active && !$fromWebhook) {
                //hem de cobrar via stripe
                $pricing = $us->pricing;
                $stripe_user = StripeUser::where([["user_id", $us->user_id], ["company_id", $pricing->company_id]])->first();
                if($stripe_user==null) throw new \Exception("Stripe user for that company and user doesn't exist.");

                if($us->renew && $payment_type->auto) {
                    //suscripcio
                    //li hem de cobrar de stripe si no ve de webhook
                    StripeRepository::payUserSubscription($stripe_user, $us, $start_date); //paguem a stripe
                }
                else {
                    //pago unic
                    $stripe_user->charge(intval($payment->amount*100), $stripe_user->stripe_payment_method_id, [
                        "description" => $pricing->recharge_weekly ? "Pago único para la tarifa " . $pricing->name . " por " . $pricing->days . " días más" : "Pago para el bono " . $pricing->name,
                        "metadata" => [
                            "pricing_id" => $pricing->id,
                            "stripe_user_id" => $stripe_user->id
                        ]
                    ]);
                }
            }

            PaymentRepository::pay($payment->id,$payment_type->id);

            DB::commit();
            return UserSubscriptionRepository::find($id);
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public static function paymentToPay($id) {
        return DB::table("user_subscriptions as us")
            ->select("p.*")
            ->leftJoin("box_order_lines as bol","bol.user_subscription_id","us.id")
            ->leftJoin("box_orders as bo","bo.id","bol.box_order_id")
            ->leftJoin("payments as p","p.id","bo.payment_id")
            ->where([
                ["us.id",$id],
                ["p.charged",0],
            ])
            ->first();
    }

    public static function resetWodCounters(UserSubscription $us) {
        $us->wod_counter = $us->pricing->wod_counter;
        $us->open_counter = $us->pricing->open_counter;
        $us->total_counter = $us->pricing->total_counter;
        $us->save();
    }

    public static function createUserSubscriptionBoxOrder($subscription_id, $user_id, $box_id, $pricing, $discount_id = null) {
        // Generem la linia del box order
        $bol = [
            "user_subscription_id" => $subscription_id,
            "quantity" => 1,
            "price" => $pricing->price,
            "vat" => Box::find($box_id)->tax_prcnt_box_order,
            "discount_id" => $discount_id
        ];
        // Generem box order
        $bo = [
            "box_id" => $box_id,
            "user_id" => $user_id,
            "lines" => [$bol],
            "discount_id" => null
        ];

        return BoxOrderRepository::create($bo);
    }

    public static function cancel($id) {
        $us = UserSubscriptionRepository::find($id);
        if($us->stripe_id!=null && $us->renew && $us->ends_at==null && $us->pricing->recharge_weekly) {
            DB::beginTransaction();
            try {
                $us->cancel();
                $us->renew = false;
                $us->save();
                DB::commit();
            }
            catch (\Exception $e) {
                DB::rollBack();
                throw $e;
            }
        }
        else throw new \Exception("La suscripción no puede ser cancelada ya que no está activada la renovación automática.");
    }

    public static function isUserSubscriptionNearlyDue(array $s) {
        $now = Carbon::now();
        return $s["active"] && $now < $s["end_date"] && $now->diffInDays($s["end_date"]) < 7;
    }

    public static function isUserSubscriptionInExtensionStatus(array $s) {
        $now = Carbon::now();
        return $s["active"] && $now > $s["end_date"];
    }

    public static function status($id) {
        $us = UserSubscriptionRepository::find($id)->toArray();
        if(UserSubscriptionRepository::isUserSubscriptionNearlyDue($us)) return UserSubscriptionRepository::SUBSCRIPTION_STATUS_NEARLY_DUE;
        else if(UserSubscriptionRepository::isUserSubscriptionInExtensionStatus($us)) return UserSubscriptionRepository::SUBSCRIPTION_STATUS_EXTENSION;
        else if($us["active"]==false) return UserSubscriptionRepository::SUBSCRIPTION_STATUS_DUE;
        else if($us["active"]==true) return UserSubscriptionRepository::SUBSCRIPTION_STATUS_ACTIVE;
        else throw new \Exception("User Subscription status not recognized");
    }

    /**
     * METODES PRIVATS
     */

    private static function getCredits($pricing_id){
        return Pricing::select('wod_counter','open_counter','total_counter')->where("id",$pricing_id)->first();
    }

}
