<?php

namespace Tests\Feature;

use App\Models\Company; 
use App\Models\CompanySubscriptionRates; 
use App\Models\CompanySubscriptions;
use App\Models\CompanyUser;
use App\Models\Worker;
use App\Modules\Company\Subscription\Payments\UpgradePayment;
use App\Modules\Company\Subscription\Payments\RenewPayment;
use App\Modules\Company\Subscription\Subscription as CompanySubscriptionModule; 

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;

use Carbon\Carbon;

use Laravel\Sanctum\Sanctum;

use Stripe;

use Tests\TestCase;

class CompanySubscriptionTest extends TestCase
{
    use RefreshDatabase;

    private Worker                      $workerForCompanyUpgrade;
    private Worker                      $workerForCompanyRenew;
    private Company                     $companyForUpgrade; 
    private Company                     $companyForRenew;
    private CompanySubscriptionRates    $companySubscriptionRateInital; 
    private CompanySubscriptionRates    $companySubscriptionRateUpgraded;

    /**
     * Set Up Test
     * 
     * @return void 
     */
    protected function setUp(): void
    {
        parent::setUp();

        //! Setup Stripe 
        Stripe\Stripe::setApiKey(config('stripe.secret_key'));

        //! Set up Company Subscriptions 
        $this->companySubscriptionRateInital = $this->createCompanySubscriptionRate(
            factoryState : 
            [ 
                'max_employee' => 5,
            ]
        );

        $this->companySubscriptionRateUpgraded = $this->createCompanySubscriptionRate(
            factoryState : 
            [ 
                'max_employee' => 50,
            ]
        );

        //! Setup Company Upgrade 
        $this->companyForUpgrade       = $this->createCompany();
        $this->workerForCompanyUpgrade = $this->createWorker();

        $this->createCompanyUser(
            factoryState: 
            [
                'company_id' => $this->companyForUpgrade->id,
                'worker_id'  => $this->workerForCompanyUpgrade->id
            ]
        );

        $this->createCompanySubscription(
            [
                "company_id"                =>  $this->companyForUpgrade->id,
                "subscription_rate_id"      =>  $this->companySubscriptionRateInital->id,
                "mode_of_payment"           =>  "monthly",
                "current_month_paid"        =>  Carbon::now()->subMonth()->format('Y-m-d')
            ]
        ); 

        //! Setup Company Renew
        $this->companyForRenew       = $this->createCompany();
        $this->workerForCompanyRenew = $this->createWorker();

        $this->createCompanyUser(
            factoryState: [
                'company_id' => $this->companyForRenew->id,
                'worker_id'  => $this->workerForCompanyRenew->id
            ]
        );

        $this->createCompanySubscription(
            [
                "company_id"           => $this->companyForRenew->id,
                "subscription_rate_id" => $this->companySubscriptionRateInital->id,
                "mode_of_payment"      => "monthly",
                "current_month_paid"   => Carbon::now()->subMonth()->format('Y-m-d'),
                "duration"             => Carbon::now()->subMonths(rand(1, 12))
            ]
        ); 
    }

    /**
     * Create Company via factory
     * 
     * @return Company 
     */
    private function createCompany(): Company
    {
        return Company::factory()->create();
    }

    /**
     * Create Company Subscription Rate via factory
     * 
     * @param array $factoryState
     * 
     * @return CompanySubscriptionRates 
     */
    private function createCompanySubscriptionRate(array $factoryState = []): CompanySubscriptionRates
    {
        return CompanySubscriptionRates::factory()
            ->state($factoryState)
            ->create();
    }

    /**
     * Create Company Subscription Rate via factory
     * 
     * @param array $factoryState
     * 
     * @return CompanySubscriptions 
     */
    private function createCompanySubscription(array $factoryState = []): CompanySubscriptions
    {
        return  CompanySubscriptions::factory()
            ->state($factoryState)
            ->create();
    }

    /**
     * Create Company User via factory
     * 
     * @param array $factoryState
     * 
     * @return CompanyUser 
     */
    private function createCompanyUser(array $factoryState = []): CompanyUser
    {
        return  CompanyUser::factory()
            ->state($factoryState)
            ->create();
    }

    /**
     * Create Worker via factory  
     * 
     * @param array $factoryState 
     * 
     * @return Worker 
     */
    private function createWorker(array $factoryState = []): Worker
    {
        return Worker::factory()
            ->state($factoryState)
            ->create();
    }

    /**
     * Test Fetch Details
     * 
     * @return void 
     */
    public function test_fetch_details(): void
    {
        Sanctum::actingAs(
            $this->workerForCompanyUpgrade,
            ['*']
        );

        $response = (new CompanySubscriptionModule())->fetchCompanySubscsriptionDetails();

        $this->assertArrayHasKey('company_subscription',    $response);
        $this->assertArrayHasKey('remaining_employee',      $response);
        $this->assertArrayHasKey('current_employee_count',  $response);

        $this->assertTrue($response['company_subscription']->subscription_rate_id   == $this->companySubscriptionRateInital->id);
        $this->assertTrue($response['company_subscription']->company_id             == $this->companyForUpgrade->id);

        $this->assertTrue($response['remaining_employee']     == 5);
        $this->assertTrue($response['current_employee_count'] == 0);
    }

    /**
     * Test Fetch Rates 
     * 
     * @return void 
     */
    public function test_fetch_rates(): void
    {
        Sanctum::actingAs(
            $this->workerForCompanyUpgrade,
            ['*']
        );

        $response = (new CompanySubscriptionModule())->getAllSubscsriptionRates();

        $this->assertTrue($response->count() >= 1);
    }

    /**
     * Test Fetch Upgraded Subscriptions 
     * 
     * @return void 
     */
    public function test_fetch_upgradedable_rates() : void 
    {
        Sanctum::actingAs(
            $this->workerForCompanyUpgrade,
            ['*']
        );

        $response = (new CompanySubscriptionModule())->showUpgradedSubscriptionRate();

        $this->assertTrue($response->count() >= 1);
    }

    /**
     * Test Subscription Upgrade 
     * 
     * @return void 
     */
    public function test_upgrade(): void
    {
        Sanctum::actingAs(
            $this->workerForCompanyUpgrade, 
            ['*']
        );

        $stripeToken = \Stripe\Token::create(
            [
                "card" =>
                [
                    "number"    => "4242 4242 4242 4242",
                    "exp_month" => "04",
                    "exp_year"  => "24",
                    "cvc"       => "242",
                    "name"      => "test"
                ]
            ]
        );

        $response = (new UpgradePayment())->upgradeSubscription(
            [    
                "mode"                       => "monthly",
                "token"                      => $stripeToken,
                "selectedSubscriptionRateID" => $this->companySubscriptionRateUpgraded->id
            ]
        );
        
        $this->assertTrue($response['result'] == 'succeeded', 'Subscription Purchase Success');   

        $this->assertDatabaseHas(
            CompanySubscriptions::class, 
            [
                'subscription_rate_id' => $this->companySubscriptionRateUpgraded->id,
                'company_id'           => $this->companyForUpgrade->id
            ]
        );
    }

    /**
     * Test Subscription Renewal
     * 
     * @return void 
     */
    public function test_renewal(): void
    {
        Sanctum::actingAs(
            $this->workerForCompanyRenew,
            ['*']
        );

        $stripeToken = \Stripe\Token::create(
            [
                "card" =>
                [
                    "number"    => "4242 4242 4242 4242",
                    "exp_month" => "04",
                    "exp_year"  => "24",
                    "cvc"       => "242",
                    "name"      => "test"
                ]
            ]
        );

        $response = (new RenewPayment())->renewSubscription(
            [
                "mode"                 => "monthly",
                "token"                => $stripeToken,
                "subscription_rate_id" => $this->companySubscriptionRateUpgraded->id
            ]
        );

        $this->assertTrue($response['result'] == 'succeeded', 'Subscription Renewal Success');

        $this->assertDatabaseHas(
            CompanySubscriptions::class,
            [
                'subscription_rate_id' => $this->companySubscriptionRateUpgraded->id,
                'company_id'           => $this->companyForRenew->id
            ]
        );
    }

}
