<?php

namespace Ignite\Core\Listeners;

use Exception;
use Ignite\Core\Contracts\Imports\ImportResolver;
use Ignite\Core\Entities\Transaction;
use Ignite\Core\Entities\User;
use Ignite\Core\Events\EnrollmentComplete;
use Ignite\Core\Entities\TransactionResource;
use Ignite\Core\Models\Import\Resolvers\UserResolver;
use Ignite\Core\Models\Transformers\TransactionResourceTransformer;
use Illuminate\Database\Eloquent\Collection;
use Throwable;

class AttachWaitingTransactions
{
    /**
     * @var TransactionResourceTransformer
     */
    private $transformer;

    /**
     * Create the event listener.
     *
     * @param TransactionResourceTransformer $transformer
     */
    public function __construct(TransactionResourceTransformer $transformer)
    {
        $this->transformer = $transformer;
    }

    /**
     * Handle the event.
     *
     * @param EnrollmentComplete $event
     *
     * @return void
     * @throws Throwable
     */
    public function handle(EnrollmentComplete $event)
    {
        if (isset($event->user)) {
            $this->attachTransactions($event->user);
        }
    }

    /**
     * Attach any existing transactions for the given user in the resource table.
     *
     * @param User $user
     *
     * @return void
     * @throws Throwable
     */
    protected function attachTransactions(User $user)
    {
        Transaction::query()->getConnection()->transaction(function () use ($user) {
            $this->findResourceTransactions($user)
                ->map(function ($resource) use ($user) {
                    $transaction = $this->transformer->transform($resource, $user);
                    $resource->delete();
                    return $transaction;
                })->each(function ($transaction) {
                    $transaction->save();
                });
        });
    }

    /**
     * Find any existing resource transactions associated with the given email address.
     *
     * @param User $user
     *
     * @return Collection
     * @throws Exception
     */
    protected function findResourceTransactions(User $user)
    {
        $identifier = $this->resolveIdentifier($user);

        if (! $identifier) {
            return new Collection([]);
        }

        return TransactionResource::query()
            ->where('identifier', $identifier)
            ->get();
    }

    /**
     * Resolve the identifier from the User classs.
     *
     * @param User $user
     *
     * @return string|bool
     * @throws Exception
     */
    protected function resolveIdentifier(User $user)
    {
        /** @var ImportResolver $userResolver */
        $userResolver = app(config('core.transaction.resource.resolvers.user') ?? $this->getDefaultUserResolver());

        if (! $userResolver instanceof ImportResolver) {
            logger()->critical(sprintf(
                'The user resolver must be an instance of %s. %s provided.',
                ImportResolver::class,
                is_object($userResolver) ? get_class($userResolver) : gettype($userResolver)
            ));

            return false;
        }

        return $userResolver->resolve($user);
    }

    /**
     * The default import user resolver.
     *
     * @return string
     */
    protected function getDefaultUserResolver()
    {
        return UserResolver::class;
    }
}
