<?php

namespace App\Modules\Company\Account;

use App\Models\Company;
use App\Models\CompanySubscriptions;
use App\Models\CompanyExpertise;
use App\Models\IECMembers;
use App\Models\Worker;
use App\Models\CompanySubscriptionRates;
use App\Models\CompanySettings;

use App\Modules\Company\Subscription\Subscription;
use App\Modules\Company\Subscription\Discount\Coupon;
use App\Modules\Company\Subscription\Payments\FirstSubscriptionPayment;

use App\Traits\UserTraits;
use App\Traits\ValidatorTraits;
use App\Traits\ZipcodeTraits;
use App\Traits\MobileNumberValidatorTraits;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class Setup
{
    use UserTraits, ValidatorTraits, MobileNumberValidatorTraits, ZipcodeTraits;
    public $coupon;
    public $subscription;
    public $firstPayment;

    public function __construct()
    {
        $this->coupon = new Coupon;
        $this->subscription = new Subscription;
        $this->firstPayment = new FirstSubscriptionPayment;
    }

    public function fetchRegistrationData()
    {
        $userData = $this->getCurrentUser();
        return [
            'companyData' => $this->fetchCompanyData($userData->company_id),
            'subscriptionRates' => $this->subscription->getAllSubscsriptionRates(),
        ];
    }

    public function fetchCompanyData($companyID)
    {
        return Company::select(
            'companies.id',
            'companies.name',
            'companies.email_address',
            'companies.discount',
            'companies.company_tel as mobile',
            'companies.company_tel2 as telephone',
            'companies.street_address as street',
            'companies.city',
            'companies.state',
            'companies.zipcode',
            'companies.contact_person_name as contactPerson',
            'companies.contact_person_no as contactPersonNumber',
            'company_subscriptions.duration',
            )
            ->with('company_expertises')
            ->leftJoin('company_subscriptions', 'company_subscriptions.company_id', 'companies.id')
            ->where('companies.id', $companyID)
            ->first();
    }   

    public function finalSetup($payload)
    {
        $currentUser = $this->getCurrentUser();
        $companyID = $currentUser->company_id;

        $rules = array(
            'companyName' => 'required|max:100|unique:companies,name,' . $companyID,
            'companyEmailAddress' => 'required|max:191|unique:companies,email_address,' . $companyID,
            'companyMobileNumber' => 'required|max:50',
            'street' => 'required|max:100',
            'city' => 'required|max:100',
            'state' => 'required|max:100',
            'zipcode' => 'required|string|min:5|max:5',
            'contactPerson' => 'required|max:100',
            'contactPersonNumber' => 'required|max:50',
            'subscriptionRateID' => 'required',
            'modeOfPayment' => 'required',
            'subscribedZipcode' => 'required',
            'subscribedZipcodeRadius' => 'required',
        );

        if (!$payload->freeSubscription) {
            $rules['token'] = 'required';
        }

        $validate = $this->validateRequest($payload, $rules);
        if ($validate) {
            return $validate;
        }

        $validate = $this->validatePhoneNumber($payload->companyMobileNumber);
        if ($validate) {
            return response()->json(['errors' => ["Sorry, you have used non-existing US mobile number on your company mobile."]])->setStatusCode(201);
        }

        $validate = $this->validatePhoneNumber($payload->contactPersonNumber);
        if ($validate) {
            return response()->json(['errors' => ["Sorry, you have used non-existing US mobile number on your contact person mobile."]])->setStatusCode(201);
        }

        $validate = $this->validateZipcodeIfExisting($payload->zipcode);
        if ($validate) {
            return $validate;
        }

        $validate = $this->validateZipcodeIfExisting($payload->subscribedZipcode);
        if ($validate) {
            return response()->json(['errors' => ["Sorry, you have subcribed a ZIP code that is unsupported in our current US ZIP code database."]])->setStatusCode(201);
        }

        // Retrieve subscription rate data
        $subscriptionRateDetails = CompanySubscriptionRates::where('id', $payload->subscriptionRateID)->first();
        if (!$subscriptionRateDetails) {
            return response()->json(['errors' => 'Subscription rate does not exist.']);
        }
    
        $couponData = false;
        // Coupon Discount
        if ($payload->couponCode) {
            $couponData = $this->coupon->fetchCouponDetails($payload->couponCode);
            if($couponData->for_all == false){
                if($couponData->company_subscription_rates_id != $subscriptionRateDetails->id){
                    return response()->json(['errors' => 'Coupon code is not applicable on the choosen subscription rate.']); 
                }
            }
            if ($couponData->is_disabled) {
                return response()->json(['errors' => 'Coupon code is currently disabled.']);
            }

            $companyUsage = $this->coupon->checkCompanyUsage($companyID);
            //how many times the company can use the coupon
            if($companyUsage && $companyUsage->coupon_subscription_rates_id == $couponData->coupon_subscription_rates_id){
                if($companyUsage->company_usage >= $companyUsage->range){
                    return response()->json(['error' => 'You have already exceeded the maximum monthly range of the coupon.']);
                }
            }
         }

        //Check if company has an active company subscription
        // $isSubscribed = $this->checkCompanySubscription($currentUser->company_id);
        // if ($isSubscribed) {
        //     return response()->json(['error' => 'Company is already subscribed. Please reload the page.']);
        // }


        // Update Company
        $companyData = array(
            'name' => ucwords($payload->companyName),
            'email_address' => $payload->companyEmailAddress,
            'company_tel' => $this->databaseFormatNumber($payload->companyMobileNumber),
            'company_tel2' => $this->databaseFormatNumber($payload->companyTelephoneNumber),
            'street_address' => $payload->street,
            'city' => $payload->city,
            'state' => $payload->state,
            'zipcode' => $payload->zipcode,
            'contact_person_name' => ucwords($payload->contactPerson),
            'contact_person_no' => $this->databaseFormatNumber($payload->contactPersonNumber),
            'type' => $payload->associatedIEC ? 'pending' : 'guest',
            'overhead_charge' => $payload->billableAmount,
            'overhead_type' => $payload->billableType,
        );

        //Update Company Expertises
        $expertises = $payload->expertises;
        $companyExpertiseTransactionList = [];

        if($expertises){       
            CompanyExpertise::where('company_id', $companyID)->delete();

            foreach($expertises as $expertise){
                $companyExpertise = array(
                    'expertise_id' => $expertise,
                    'company_id' => $companyID,
                );
                array_push($companyExpertiseTransactionList, $this->updateCompanyExpertise($companyExpertise));
            }
        }
        

        $companyTransaction = $this->updateCompany($companyID, $companyData);

        //Save IEC MemberData
        if ($payload->associatedIEC) {
            $IECData = [
                'company_id' => $companyID,
                'chapter_name' => $payload->associatedIEC,
            ];
            IECMembers::create($IECData);
        }

        // Company Settings : 3 settings
        $settingsData = [
            'company_id' => $companyID,
            'key'  => 'worker_request_email_notification',
            'value'  => 'enabled',
        ];
        $this->createCompanySetting($settingsData);
      
        $settingsData = [
            'company_id' => $companyID,
            'key'  => 'subscribed_zipcode',
            'value'  => $payload->subscribedZipcode,
        ];
        $this->createCompanySetting($settingsData);
       
        $settingsData = [
            'company_id' => $companyID,
            'key'  => 'subscribed_zipcode_radius',
            'value'  => $payload->subscribedZipcodeRadius,
        ];
        $this->createCompanySetting($settingsData);


        //if company is update. process first payment
        foreach($companyExpertiseTransactionList as $companyExpertiseTransaction){
            if (!$companyTransaction || !$companyExpertiseTransaction) {
                return response()->json(['error' => 'Something went wrong. Please try again later']);
            }
        }
        return $this->processFirstPayment($payload, $subscriptionRateDetails, $currentUser, $couponData);
        
    }

    public function updateCompany($companyID, $data)
    {
        return Company::where('id', $companyID)->update($data);
    }
    
    public function updateCompanyExpertise($data)
    {
        return CompanyExpertise::create($data);
    }

    public function createCompanySetting($settingsData){
        return CompanySettings::create($settingsData);
    }

    public function processFirstPayment($payload, $subscriptionRateDetails, $currentUser, $couponData)
    {

        $companyDetails = Company::select('discount')->where('id', $currentUser->company_id)->first();
        $couponDiscount = 0;
        $companyDiscount = 0;

        //Subscription payment computation
        $subcriptionDurationDate = Carbon::now()->addDays(360)->format('Y-m-d h:i:s');
        $monthlySubscriptionDuration = 0;

        if ($payload->modeOfPayment == 'monthly') {
            $subscriptionRatePrice = (float) $subscriptionRateDetails->monthly_rate;
            $monthlySubscriptionDuration = Carbon::now()->addDays(30)->format('Y-m-d h:i:s');

            // Company Discount Discount
            if ($companyDetails->discount) {
                $companyDiscount = $this->getDiscountedValue($companyDetails->discount, $subscriptionRatePrice);
            }

            // Coupon Discount
            if ($payload->couponCode) {
                if($couponData->type == 'fixed'){
                    $couponDiscount = (float) $couponData->discount;
                }else{
                    $couponDiscount = $this->getDiscountedValue($couponData->discount, $subscriptionRatePrice);
                }
            }
        } else if ($payload->modeOfPayment == 'yearly') {
            $subscriptionRatePrice = (float) $subscriptionRateDetails->yearly_rate;

            // Company Discount
            if ($companyDetails->discount) {
                $companyDiscount = $this->getDiscountedValue($companyDetails->discount, $subscriptionRatePrice);
            }

            // Coupon Discount
            if ($payload->couponCode) {
                if($couponData->type == 'fixed'){
                    $couponDiscount = (float) $couponData->discount * 12;
                }else{
                    $couponDiscount = $this->getDiscountedValue($couponData->discount, $subscriptionRatePrice);
                }
            }
        }

        $amountToPay = $subscriptionRatePrice - ($companyDiscount + $couponDiscount);
        $amountDue = 0;
        if ($amountToPay <= 0) {
            $amountDue = $amountToPay;
            $amountToPay = 0;
        }

        $paymentPayload = (object) [
            'mode' => $payload->modeOfPayment,
            'stripeToken' => $payload->token,
            'subscriptionRatePrice' => $subscriptionRatePrice,
            'subcriptionRateID' => $subscriptionRateDetails->id,
            'subcriptionRateName' => $subscriptionRateDetails->name,
            'companyDiscount' => $companyDiscount,
            'couponDiscount' => $couponDiscount,
            'amountToPay' => $amountToPay,
            'amountDue' => $amountDue,
            'currentUserID' => $currentUser->id,
            'currentUserEmailAddress' => $currentUser->email_address,
            'companyID' => $currentUser->company_id,
            'subcriptionDurationDate' => $subcriptionDurationDate,
            'totalDiscount' => $companyDiscount + $couponDiscount,
            'dateTransacted' => Carbon::now()->format('Y-m-d h:i:s'),
            'couponCode' => $payload->couponCode,
            'couponData' => $couponData ? $couponData : null,
            'monthlySubscriptionDuration' => $monthlySubscriptionDuration,
        ];

        // return response()->json($paymentPayload);
        return $this->firstPayment->stripePayment($paymentPayload);
    }

    public function getDiscountedValue($percentage, $amountToBePercentage)
    {
        return (float) ($percentage / 100) * $amountToBePercentage;
    }

    public function verify($payload)
    {
        $emailVerificationCode = $payload->code;
        $emailVerification = Worker::select('email_address')
            ->where('email_verification_code', $emailVerificationCode)
            ->limit(1)
            ->get();
        if ($emailVerification->isEmpty()) {
            return response()->json(['errors' => ['Token not found.']])
                ->setStatusCode(201);
        } else {
            return response()->json([
                'success' => ['Token found.'],
                'email_address' => $emailVerification[0]->email_address,
            ])->setStatusCode(201);
        }
    }

    public function setup($payload)
    {
        // start transaction
        DB::beginTransaction();
        $password = Hash::make($payload->password);
        $emailAddress = $payload->email_address;
        $emailVerificationCode = $payload->token;
        
        $userData = array(
            'email_verification_code' => null,
            'password' => $password,
            'email_verified_at' => Carbon::now(),
        );
        $updateWorkerTransaction = Worker::where('email_address', $emailAddress)
            ->where('email_verification_code', $emailVerificationCode)
            ->update($userData);

        // if there's an error or queries don't do their job, rollback!
        if (!$updateWorkerTransaction) {
            DB::rollBack();
            return response()->json(['error' => 'Server error.', "transaction" => $updateWorkerTransaction]);
        } else {
            DB::commit();
            return response()->json(['success' => 'Data updated successfully.'])->setStatusCode(201);
        }
    }

    public function checkCompanySubscription($companyID){
        return CompanySubscriptions::where('company_id', $companyID)->first();
    }

}
