<?php

namespace App\Classes\Modules\Accounts\Processors;

use App\Classes\Modules\Accounts\DataTransferObjects\CrossAuthenticationCredentialsObject;
use App\Classes\Modules\Companies\DataTransferObjects\CompanyConnectionObject;
use App\Classes\Modules\Companies\DataTransferObjects\EmploymentObject;
use App\Classes\Modules\Accounts\DataTransferObjects\UserSocialAccountObject;

use App\Classes\Modules\Accounts\Services\CrossAuthenticatesUser;
use App\Classes\Modules\Accounts\Services\AuthenticationRedirect;
use App\Classes\Modules\Accounts\Services\FetchesUser;
use App\Classes\Modules\Accounts\Services\GeneratesAuthenticationToken;
use App\Classes\Modules\Accounts\Standards\Rules\CanCrossAuthenticateUser;
use App\Classes\Modules\Contacts\DataTransferObjects\ContactObject;

use App\Classes\Modules\Accounts\Processors\CreateUserProcessor;
use App\Classes\Modules\Companies\Processors\CreateCompanyProcessor;
use App\Classes\Modules\Contacts\Processors\CreateContactProcessor;
use App\Classes\Modules\Companies\Processors\CreateCompanyModuleProcessor;
use App\Classes\Modules\Companies\Services\CreatesCompanyConnection;
use App\Classes\General\Services\GeneratesInitials;
use App\Classes\Modules\Companies\Services\ApprovesCompanyConnection;
use App\Classes\Modules\Companies\Processors\AssignEmployeeProcessor;
use App\Classes\Modules\Documents\Processors\UploadIdentityDocumentProcessor;
use App\Classes\Modules\Accounts\Services\CreatesUserSocialAccount;

use App\Classes\ValueObjects\Constants\ApprovalStatus;
use App\Classes\ValueObjects\Constants\BusinessType;
use App\Classes\ValueObjects\Constants\CompanyType;
use App\Classes\ValueObjects\Constants\RoleTypes;

use Illuminate\Http\Request;
use App\Models\UserSocialAccount;

class CrossAuthenticationProcessor
{

    /** @var CanCrossAuthenticateUser */
    private $canCrossAuthenticateUser;

    /** @var CrossAuthenticatesUser */
    private $authenticatesUser;

    /** @var GeneratesAuthenticationToken */
    private $generatesAuthenticationToken;

    /** @var FetchesUser */
    private $fetchesUser;

    /** @var AuthenticationRedirect */
    private $authenticationRedirect;

    /** @var CreateUserProcessor */
    private $createUserProcessor;

    /** @var CreateCompanyProcessor */
    private $createCompanyProcessor;

    /** @var CreateContactProcessor */
    private $createContactProcessor;

    /** @var CreateCompanyModuleProcessor */
    private $createCompanyModuleProcessor;

    /** @var CreatesCompanyConnection */
    private $createsCompanyConnection;

    /** @var ApprovesCompanyConnection */
    private $approvesCompanyConnection;

    /** @var AssignEmployeeProcessor */
    private $assignEmployeeProcessor;

    /** @var UploadIdentityDocumentProcessor */
    private $uploadIdentityDocumentProcessor;

    /** @var CreatesUserSocialAccount */
    private $createsUserSocialAccount;

    /**
     * CrossAuthenticationProcessor constructor.
     * @param CanCrossAuthenticateUser $canCrossAuthenticateUser
     * @param CrossAuthenticatesUser $crossAuthenticatesUser
     * @param GeneratesAuthenticationToken $generatesAuthenticationToken
     * @param FetchesUser $fetchesUser
     * @param AuthenticationRedirect $authenticationRedirect
     * @param CreateUserProcessor $createUserProcessor
     * @param CreateCompanyProcessor $createCompanyProcessor
     * @param CreateContactProcessor $createContactProcessor
     * @param CreateCompanyModuleProcessor $createCompanyModuleProcessor
     * @param CreatesCompanyConnection $createsCompanyConnection
     * @param ApprovesCompanyConnection $approvesCompanyConnection
     * @param AssignEmployeeProcessor $assignEmployeeProcessor
     * @param UploadIdentityDocumentProcessor $uploadIdentityDocumentProcessor
     * @param CreatesUserSocialAccount $createsUserSocialAccount
     */
    public function __construct(
        CanCrossAuthenticateUser $canCrossAuthenticateUser, 
        CrossAuthenticatesUser $crossAuthenticatesUser, 
        GeneratesAuthenticationToken $generatesAuthenticationToken, 
        FetchesUser $fetchesUser, 
        AuthenticationRedirect $authenticationRedirect,
        CreateUserProcessor $createUserProcessor,
        CreateCompanyProcessor $createCompanyProcessor,
        CreateContactProcessor $createContactProcessor,
        CreateCompanyModuleProcessor $createCompanyModuleProcessor,
        CreatesCompanyConnection $createsCompanyConnection,
        ApprovesCompanyConnection $approvesCompanyConnection,
        AssignEmployeeProcessor $assignEmployeeProcessor,
        UploadIdentityDocumentProcessor $uploadIdentityDocumentProcessor,
        CreatesUserSocialAccount $createsUserSocialAccount
    )
    {
        $this->canCrossAuthenticateUser = $canCrossAuthenticateUser;
        $this->crossAuthenticatesUser = $crossAuthenticatesUser;
        $this->generatesAuthenticationToken = $generatesAuthenticationToken;
        $this->fetchesUser = $fetchesUser;
        $this->authenticationRedirect = $authenticationRedirect;
        $this->createUserProcessor = $createUserProcessor;
        $this->createCompanyProcessor = $createCompanyProcessor;
        $this->createContactProcessor = $createContactProcessor;
        $this->createCompanyModuleProcessor = $createCompanyModuleProcessor;
        $this->createsCompanyConnection = $createsCompanyConnection;
        $this->createsCompanyConnection = $createsCompanyConnection;
        $this->approvesCompanyConnection = $approvesCompanyConnection;
        $this->assignEmployeeProcessor = $assignEmployeeProcessor;
        $this->uploadIdentityDocumentProcessor = $uploadIdentityDocumentProcessor;
        $this->createsUserSocialAccount = $createsUserSocialAccount;
    }


    /**
     * @param Request $request
     * @return array
     * @throws \App\Classes\Exceptions\AccessForbiddenException
     * @throws \App\Classes\Exceptions\AccessUnauthorisedException
     * @throws \App\Classes\Exceptions\RequestValidationException
     */
    public function execute(Request $request): array {

        $object = new CrossAuthenticationCredentialsObject($request->input('access_token'), $request->input('platform'));

        $this->canCrossAuthenticateUser->passes($object);

        $cross = $this->crossAuthenticatesUser->execute($object);

        $user_social_account = UserSocialAccount::where('app_id', $cross->token->user->id)->first();
        if (!$user_social_account) {

            $request->request->add([
                'name' => $cross->token->user->name,
                'email' => $cross->token->user->email,
                'password' => 'pass123',
                'password_confirmation' => 'pass123',
            ]);

            $user = $this->createUserProcessor->execute(
                $request, 
                RoleTypes::USER, 
                env('APP_ENV') != 'production' ? ApprovalStatus::APPROVED : ApprovalStatus::PENDING_VERIFICATION
            );

            $company = $this->createCompanyProcessor->execute(
                $cross->payload->data->name,
                CompanyType::COMPANY_BUSINESS, 
                ApprovalStatus::PENDING_SUBMISSION
            );

            $contactObject = new ContactObject(
                $cross->payload->data->contact->reference, 
                $cross->payload->data->contact->phone, 
                $cross->payload->data->contact->email, 
                $cross->payload->data->contact->wechat_id
            );
            $contact = $this->createContactProcessor->execute($contactObject, $company);

            $companyModule = $this->createCompanyModuleProcessor->execute($company, BusinessType::IMPORTER);

            $connectionObject = new CompanyConnectionObject(
                $companyModule, 'CIEF', 
                mt_rand(1000, 9999).(new GeneratesInitials())->name($company->name)->length(3)->generate()
            );
            $connection = $this->createsCompanyConnection->execute($connectionObject);

            $this->approvesCompanyConnection->execute($connection);

            $object = new EmploymentObject($companyModule, $user);
            $this->assignEmployeeProcessor->execute($object);

            $request->request->add([
                'files' => [$cross->document->src],
                'identification_no' => $cross->payload->data->identification->reference,
            ]);

            $this->uploadIdentityDocumentProcessor->execute($request, $company);

            $object = new UserSocialAccountObject(
                $cross->token->user->id, 
                $user,
                $request->input('platform')
            );
            $this->createsUserSocialAccount->execute($object);
        }
        else {
            $user = $user_social_account->user()->first();
        }

        return ['access_token' => $this->generatesAuthenticationToken->execute($user), 'redirect_url' => $this->authenticationRedirect->url($user)];
    }
}