<?php


namespace App\Repositories;


use App\Company;
use App\Discount;
use App\Invoice;
use App\News;
use App\Notifications\NewCompanyNotification;
use App\StripeUser;
use App\Subscription;
use Carbon\Carbon;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use League\Flysystem\FileNotFoundException;
use Faker\Factory as Faker;
use Spatie\ImageOptimizer\OptimizerChainFactory;

class CompanyRepository {

    private const PUBLIC_PROFILE_IMAGES_FOLDER = "storage/company-logos";

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

    public static function create($c, $u, $b, $plan_id) {
        DB::beginTransaction();
        try {
            //1. crear companyia
            $company = Company::create($c);

            //2. Crear box
            $b["company_id"] = $company->id;
            $box = BoxRepository::create($b);

            //3. Crear pricing ilimitat
            PricingRepository::create([
                "company_id" => $company->id,
                "name" => "Tarifa gratuita ilimitada",
                "description" => "Tarifa gratuita ilimitada para administradores y coaches",
                "price" => null,
                "vat_included" => null,
                "wod_counter" => 100,
                "open_counter" => 100,
                "total_counter" => 100,
                "is_public" => false
            ], [$box->id]);

            //4. crear usuari administrador
            $user = UserRepository::create($u);

            //5. crear subscripcio per el plan de la companyia
            $sub = SubscriptionRepository::subscribe($company->id, $plan_id);

            //6. crear subscripcio per administrador
            $user_sub = UserSubscriptionRepository::createCompanyFirstAdminUserSubscription($company->id, $user->id, $box->id); //utilitza UserSubscriptionRepository::create()

            //7. Registrar a Stripe
            $stripe_data = $company->createAsStripeCustomer(StripeRepository::companyToStripeFormat($company));

            DB::commit();
        }
        catch(\Exception $e) {
            //gestionar transaccions
            DB::rollBack();

            throw $e;
        };

        //8. Enviar notificació
        $company->notify((new NewCompanyNotification($company, PlanRepository::find($plan_id)))->delay(Carbon::now()->addSeconds(5)));

        $user_sub->refresh();
        $user->refresh();

        return [
            "subscription" => $sub,
            "company" => $company,
            "user" => $user,
            "user_subscription" => $user_sub
        ];
    }

    public static function update(array $data, $id) {
        Company::findOrFail($id)->update($data);
        $company = Company::findOrFail($id);

        if($company->stripe_id) {
            $company->updateStripeCustomer(StripeRepository::companyToStripeFormat($company));
        }

        return $company;
    }

    public static function delete($id){
        //$company = CompanyRepository::find($id);
        //Company::destroy($id);
        CompanyRepository::cancelActivePlan($id);
        return true;
    }

    public static function find($id){
        return Company::findOrFail($id);
    }

    public static function boxes($id) {
        return Company::findOrFail($id)->boxes;
    }

    public static function pricing($id) {
        return Company::findOrFail($id)->pricing;
    }

    public static function discounts($id, bool $active = null) {
        if($active) {
            $discounts = Discount::where([
                ["company_id", $id],
                ["active", true]
            ])->get();
        }
        else {
            $discounts = Company::findOrFail($id)->discounts;
        }
        return $discounts;
    }

    public static function orders($id) {
        return Company::findOrFail($id)->box_orders;
    }

    public static function paymentTypesActive($id) {
        //TODO: mes endavant s'haura de mirar els types que depenguin dels camps de paypal de company, etc...
        return PaymentTypeRepository::active();
    }

    public static function subscriptions($id) {
        return CompanyRepository::find($id)->subscriptions;
    }

    public static function companyOrders($id) {
        return CompanyRepository::find($id)->company_orders;
    }

    public static function news($id, $params = null) {
        $sort_by = "created_at";
        $sort_dir = "desc";
        $whereClause = [["company_id", $id]];
        if($params!=null && is_array($params)) {
            if(isset($params["page_size"])) unset($params["page_size"]);
            if(isset($params["page_number"])) unset($params["page_number"]);
            if(isset($params["sort_col"])) {
                $sort_by = $params["sort_col"];
                unset($params["sort_col"]);
            }
            if(isset($params["sort_dir"])) {
                $sort_dir = $params["sort_dir"];
                unset($params["sort_dir"]);
            }
            foreach($params as $key => $val) {
                if(is_numeric($val)) array_push($whereClause, [$key, $val]);
                else array_push($whereClause, [$key, "like", "%$val%"]);
            }
        }

        return News::where($whereClause)
            ->orderBy($sort_by, $sort_dir)
            ->get();
    }


    public static function users($id, bool $active = false) {
        $res = [];
        $res_inserted = [];

        foreach(CompanyRepository::boxes($id) as $box) {
            foreach(BoxRepository::users($box['id'], $active) as $u)
                if(!in_array($u->id, $res_inserted)) { //comprovem que no es repeteixin
                    array_push($res, $u);
                    array_push($res_inserted, $u->id);
                }
        }
        return $res;
    }

    public static function exportCSV($id, $invoices_id) {

        if($invoices_id!=null) {
            $data = Invoice::whereIn('id',explode(',',$invoices_id))->where('company_id','=',$id)->get();
        } else {
            $data = CompanyRepository::find($id)->invoices;
        }

        $columns = [
            "#",
            "Identificador de la compañia",
            "Tipo",
            "Nombre fiscal del destinatario",
            "Dirección del destinatario",
            "CIF del destinatario",
            "Nombre del origen",
            "Direcciión del origen",
            "CIF del origen",
            "Prefijo",
            "Serie",
            "Numero",
            "Fecha de emisión",
            "Fecha de vencimiento",
            "Concepto",
            "Subtotal",
            "Impuestos",
            "Impuestos %",
            "Total"
        ];

        return function() use ($data, $columns)
        {
            $file = fopen('php://output', 'w');
            fputcsv($file, $columns);

            foreach($data as $row) {
                fputcsv($file, $row->toArray());
            }
            fclose($file);
        };
    }

    public static function cancelActivePlan($id) {
        $company = CompanyRepository::find($id);
        if($company->active_plan != null) { //cancelem el seu pla actiu a final de periode
            SubscriptionRepository::cancel($company->active_plan->id);
        }

        return true;
    }

    public static function getStripeIdByUserId($company_id, $user_id) {
        $u = StripeUser::where([["user_id", $user_id], ["company_id", $company_id]])->first();
        if(!$u) throw new \Exception("Stripe user not found for that user");
        return $u->stripe_id;
    }

    public static function getStripeUserByUserId($company_id, $user_id) {
        return StripeUser::where([["user_id", $user_id], ["company_id", $company_id]])->first();
    }

    /**
     * $params:
     *      => ["sort_col"]: "user_name" || "pricing" || "role_group" || "date_end" || "price"
     *      => ["sort_dir"]: "asc" || "desc"
     *      => ["subscription_status"]: 1: All || 2: Active || 3: A punto de caducar || 4: En prorroga || 5: Caducadas
     *      => ["pricing_id"]: -1: Any pricing || Specified pricing id
     *      => ["role_group"]: 1: Athletes || 2: Coaches and admins
     *      => ["user_name_like"]: string
     *      => ["renew"]: 0 o 1
     */
    public static function userSubscriptions($id, array $params = null) {
        $user_subscriptions = CompanyRepository::find($id)->user_subscriptions->toArray();
        $now = Carbon::now();
        if($params) {
            //filter
            $user_subscriptions = array_filter($user_subscriptions, function($s) use(&$params, &$now) {
                if(isset($params["subscription_status"])) {
                    switch($params["subscription_status"]) {
                        case UserSubscriptionRepository::SUBSCRIPTION_STATUS_ANY: //return all
                            //no fem res
                            break;
                        case UserSubscriptionRepository::SUBSCRIPTION_STATUS_ACTIVE: //only active
                            if(!$s["active"]) return false;
                            break;
                        case UserSubscriptionRepository::SUBSCRIPTION_STATUS_NEARLY_DUE: //a punto de caducar
                            if(!UserSubscriptionRepository::isUserSubscriptionNearlyDue($s)) return false;
                            break;
                        case UserSubscriptionRepository::SUBSCRIPTION_STATUS_EXTENSION: //en prorroga
                            if(!UserSubscriptionRepository::isUserSubscriptionInExtensionStatus($s)) return false;
                            break;
                        case UserSubscriptionRepository::SUBSCRIPTION_STATUS_DUE: //caducada
                            if($s["active"]==true) return false;
                            break;
                    }
                }

                if(isset($params["pricing_id"]) && $params["pricing_id"]!=-1) {
                    if($params["pricing_id"]!=$s["pricing_id"]) return false;
                }

                if(isset($params["role_group"])) {
                    if($params["role_group"]==1 && $s["role"]["slug"]!="athlete") return false;
                    else if($params["role_group"]==2 && ($s["role"]["slug"]!="coach" && $s["role"]["slug"]!="box_admin" && $s["role"]["slug"]!="company_admin")) return false;
                }

                if(isset($params["user_name_like"]) && $params["user_name_like"]!=null) {
                    if(
                        strpos(strtolower($s["user"]["name"]), strtolower($params["user_name_like"]))===false &&
                        strpos(strtolower($s["user"]["surnames"]), strtolower($params["user_name_like"]))===false
                    ) return false;
                }

                if(isset($params["renew"])) {
                    if($params["renew"] == 1 && $s["renew"]!=1) return false;
                    else if($params["renew"] == 0 && $s["renew"]!=0) return false;
                }

                return true;
            });

            //sort
            if(isset($params["sort_col"]) && isset($params["sort_dir"])) {
                $asc = $params["sort_dir"] === "asc";
                usort($user_subscriptions, function($s1, $s2) use(&$params, &$asc) {
                    if($params["sort_col"] === "id") {
                        if($s1["id"] === $s2["id"]) return 0;
                        else return $s1["id"] < $s2["id"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else if($params["sort_col"] === "user_name") {
                        if($s1["user"]["name"] === $s2["user"]["name"]) {
                            if($s1["user"]["surnames"] === $s2["user"]["surnames"]) return 0;
                            else return $s1["user"]["surnames"] < $s2["user"]["surnames"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                        }
                        else return $s1["user"]["name"] < $s2["user"]["name"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else if($params["sort_col"] === "pricing") {
                        if($s1["pricing_id"] === $s2["pricing_id"]) return 0;
                        else return $s1["pricing_id"] < $s2["pricing_id"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else if($params["sort_col"] === "role_group") {
                        if($s1["role_id"] === $s2["role_id"]) return 0;
                        else return $s1["role_id"] < $s2["role_id"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else if($params["sort_col"] === "date_end") {
                        if($s1["end_date"] === $s2["end_date"]) return 0;
                        else return $s1["end_date"] < $s2["end_date"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else if($params["sort_col"] === "price") {
                        if($s1["price"] === $s2["price"]) return 0;
                        else return $s1["price"] < $s2["price"] ? ($asc ? -1 : 1) : ($asc ? 1 : -1);
                    }
                    else throw new \Exception("sort_col attribute value incorrect.");
                });
            }
        }

        return $user_subscriptions;
    }

    public static function userSubscriptionsByUserId($id, $user_id) {
        return UserRepository::userSubscriptions($user_id, $id, null, ["active" => false]);
    }

    public static function uploadProfileImage($id, UploadedFile $file) {
        if($file==null) throw new FileNotFoundException("No se ha proporcionado ninguna imagen.");

        $faker = Faker::create();
        $name = $faker->uuid . "." . $file->extension();
        $path = public_path(CompanyRepository::PUBLIC_PROFILE_IMAGES_FOLDER);

        //movem
        $path = $file->move($path, $name);

        //optimitzem
        $optimizerChain = OptimizerChainFactory::create();
        $optimizerChain->optimize($path);

        //fem url
        $url = url("/" . CompanyRepository::PUBLIC_PROFILE_IMAGES_FOLDER . "/" .  $name);

        $company = Company::findOrFail($id);
        $company->logo_url = $url;
        $company->save();

        return $company;
    }

    public static function subscriptionsAndOrders($id) {
        return Subscription::with('company_orders')->where("company_id", $id)->orderByDesc("start_date")->get();
    }

    public static function createSetupIntentForUser($id, $user_id) {
        $company = CompanyRepository::find($id);
        $user = UserRepository::find($user_id);
        if(!$company->is_stripe_active) throw new \Exception("You cannot update a payment method in a company which is not active in Stripe.");

        $stripe_user = StripeUser::where([["user_id", $user_id], ["company_id", $id]])->first();
        if($stripe_user == null) {
            //create customer in stripe
            $stripe_user = StripeRepository::createStripeUserForCompany($company, $user);
        }

        //create setup intent
        $options = [];
        if($stripe_user->stripe_id!=null) {
            $options["customer"] = $stripe_user->stripe_id; //qui borri aixo el mato. att: pol
            $options["metadata"]["stripe_user_id"] = $stripe_user->id;
            $options["metadata"]["user_id"] = $stripe_user->user_id;
        }

        return $stripe_user->createSetupIntent($options);
    }

    public static function attachPaymentMethodToCustomer($stripe_customer_id, $payment_method_id, $card_brand, $card_last_four) {
        $stripe_user = StripeUser::where("stripe_id", $stripe_customer_id)->first();
        if($stripe_user==null) throw new \Exception("Stripe User not found with id $stripe_customer_id");

        $stripe_user->stripe_payment_method_id = $payment_method_id;
        $stripe_user->card_brand = $card_brand;
        $stripe_user->card_last_four = $card_last_four;
        $stripe_user->save();

        return true;
    }

    public static function detachPaymentMethod($payment_method_id) {
        $stripe_user = StripeUser::where("stripe_payment_method_id", $payment_method_id)->first();
        if($stripe_user==null) throw new \Exception("Stripe User with payment method $payment_method_id not found.");

        $stripe_user->stripe_payment_method_id = null;
        $stripe_user->card_brand = null;
        $stripe_user->card_last_four = null;
        $stripe_user->save();

        return true;
    }


}
