<?php

namespace Ignite\Core\Services\Importers;

use Ignite\Core\Entities\Participant;
use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Arr;
use Illuminate\Validation\Validator;
use Ignite\Core\Contracts\Importable;
use Ignite\Core\Models\Import\Importer as BaseImporter;
use Ignite\Core\Repositories\ParticipantRepository;

class Participants extends BaseImporter implements Importable
{
    public const USER_COLUMN = 'email';

    /**
     * The required data that must be available in the file.
     *
     * @var array
     */
    protected $validation = [
        //'user_id'          => 'required',
        'first'            => 'required',
        'last'             => 'required',
        'email'            => 'required|email',
        'type'             => 'required',
    ];

    /**
     * The list of "known" fields.
     *
     * @var array
     */
    protected $fields = [
        'user_id' => ['user', 'id'],
        'first'   => ['firstname', 'first_name', 'forename', 'participant_first'],
        'last'    => ['lastname', 'last_name', 'surname', 'participant_last'],
        'email'   => ['email_address', 'e_mail', 'e-mail'],
        'type'    => ['participant_type'],
    ];

    /**
     * @var ParticipantRepository
     */
    protected $participantRepository;

    /**
     * Construct an instance of the Participant importer.
     *
     * @param ParticipantRepository $participantRepository
     */
    public function __construct(ParticipantRepository $participantRepository)
    {
        $this->participantRepository = $participantRepository;
    }

    /**
     * Return the column header in the csv file to use in order to identify the user.
     *
     * @return string
     */
    protected function getUserIdentifier()
    {
        return 'email';
    }

    /**
     * Prepare any dependencies for each iteration of the import process.
     *
     * @return void
     * @throws \League\Csv\Exception
     */
    public function prepare()
    {
        $this->cacheUsers(static::USER_COLUMN, $this->getUserIdentifier());
        $this->record->deleteLog();
    }

    /**
     * Validate a single line.
     *
     * @param  array $data
     * @return Validator
     */
    public function validate(array $data): Validator
    {
        return $this->createValidatorInstance($data, $this->validation);
    }

    /**
     * Transform the data for the given line.
     *
     * @param  array $line
     * @return array
     */
    public function transform(array $line): array
    {
        $line['user_id'] = $this->transformUser($line, static::USER_COLUMN);

        return $line;
    }

    /**
     * Add a success message to the log file.
     *
     * @param  int   $line
     * @param  array $data
     * @return string
     */
    public function formatImportMessage(int $line, array $data): string
    {
        return sprintf(
            'Imported line `%s` for participant identified by %s `%s`',
            $line,
            static::USER_COLUMN,
            Arr::get($data, static::USER_COLUMN, 'Missing')
        );
    }

    /**
     * Add an error message to the log file.
     *
     * @param  int        $line
     * @param  array      $data
     * @param  \Exception $error
     * @return string
     */
    public function formatRejectMessage(int $line, array $data, \Throwable $error): string
    {
        return sprintf(
            'Rejected line `%s` for participant identified by %s `%s` - Error: %s',
            $line,
            $this->getUserIdentifier(),
            Arr::get($data, static::USER_COLUMN, 'Missing'),
            $this->formatRejectError($error)
        );
    }

    /**
     * Process the data for a single line.
     *
     * @param  array $data
     * @return bool
     */
    public function save(array $data): bool
    {
        $data = collect($data)->except('user_id')->toArray();

        if (array_key_exists($data['email'], $this->cache['users'])) {
            $participantId = $this->cache['users'][$data['email']];
            $participant = $this->participantRepository->update($participantId, $data);
        } else {
            $participant = $this->participantRepository->create($data);
            event(new Registered($participant->user));
            $this->cache['users'][$data['email']] = $participant->user_id;
        }

        return $participant instanceof Participant ? true : false;
    }

    /**
     * Transform the user identifier to ID but throw an exception with an unknown user.
     *
     * @param  array  $line
     * @param  string $key
     * @return int|null
     */
    protected function transformUser($line, $key)
    {
        if (! array_key_exists($key, $line)) {
            throw new \DomainException("Unable to locate the header with name: $key");
        }

        $users = $this->cache['users'];
        if (! array_key_exists($line[$key], $users)) {
            return '';
        }

        return $users[$line[$key]];
    }
}
