<?php

namespace App\Http\Controllers;

use App\Models\User\User;
use App\Helpers\DateHelper;
use App\Models\Contact\Tag;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Helpers\LocaleHelper;
use App\Helpers\AccountHelper;
use App\Helpers\TimezoneHelper;
use App\Models\Contact\Contact;
use App\Jobs\AddContactFromVCard;
use App\Models\Account\ImportJob;
use App\Models\Account\Invitation;
use App\Services\User\EmailChange;
use App\Exceptions\StripeException;
use App\Http\Requests\ImportsRequest;
use Illuminate\Http\RedirectResponse;
use App\Notifications\InvitationMail;
use App\Http\Requests\SettingsRequest;
use App\Services\Contact\Tag\UpdateTag;
use LaravelWebauthn\Models\WebauthnKey;
use App\Http\Requests\InvitationRequest;
use App\Services\Contact\Tag\DestroyTag;
use App\Services\Account\Settings\ResetAccount;
use App\Services\Account\Settings\DestroyAccount;
use PragmaRX\Google2FALaravel\Facade as Google2FA;
use App\Http\Resources\Contact\ContactShort as ContactResource;
use App\Http\Resources\Settings\WebauthnKey\WebauthnKey as WebauthnKeyResource;

class SettingsController extends Controller
{
    /**
     * Instantiate a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('limitations')->only(['inviteUser', 'storeImport']);
    }

    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\View\View
     */
    public function index()
    {
        $meContact = null;

        $search = auth()->user()->first_name.' '.
            auth()->user()->last_name.' '.
            auth()->user()->email;
        $existingContacts = Contact::search($search, auth()->user()->account_id, 'id')
            ->real()
            ->whereNotIn('id', [auth()->user()->me_contact_id])
            ->paginate(20);

        if (auth()->user()->me_contact_id) {
            $meContact = Contact::where('account_id', auth()->user()->account_id)
                ->find(auth()->user()->me_contact_id);
            if ($meContact) {
                $existingContacts->prepend($meContact);
            }
        }

        $accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);

        return view('settings.index')
                ->withAccountHasLimitations($accountHasLimitations)
                ->withMeContact($meContact ? new ContactResource($meContact) : null)
                ->withExistingContacts(ContactResource::collection($existingContacts))
                ->withNamesOrder(User::NAMES_ORDER)
                ->withLocales(LocaleHelper::getLocaleList()->sortByCollator('name-orig'))
                ->withHours(DateHelper::getListOfHours())
                ->withSelectedTimezone(TimezoneHelper::adjustEquivalentTimezone(DateHelper::getTimezone()))
                ->withTimezones(collect(TimezoneHelper::getListOfTimezones())->map(function (array $timezone): array {
                    return ['id' => $timezone['timezone'], 'name'=>$timezone['name']];
                }));
    }

    /**
     * Save user settings.
     *
     * @param  SettingsRequest  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function save(SettingsRequest $request)
    {
        $user = $request->user();

        $user->update(
            $request->only([
                'first_name',
                'last_name',
                'timezone',
                'locale',
                'currency_id',
                'name_order',
                'fluid_container',
                'temperature_scale',
            ])
        );

        if ($user->email !== $request->input('email')) {
            app(EmailChange::class)->execute([
                'account_id' => $user->account_id,
                'email' => $request->input('email'),
                'user_id' => $user->id,
            ]);
        }

        if (! AccountHelper::hasLimitations($user->account) && $request->input('me_contact_id')) {
            $user->me_contact_id = $request->input('me_contact_id');
            $user->save();
        }

        $user->account->default_time_reminder_is_sent = $request->input('reminder_time');
        $user->account->save();

        return redirect()->route('settings.index')
            ->with('status', trans('settings.settings_success', [], $request['locale']));
    }

    /**
     * Delete user account.
     *
     * @param  Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function delete(Request $request)
    {
        $account = auth()->user()->account;

        try {
            app(DestroyAccount::class)->execute([
                'account_id' => $account->id,
            ]);
        } catch (StripeException $e) {
            return redirect()->route('settings.index')
                ->withErrors($e->getMessage());
        }

        auth('')->logout();

        return redirect()->route('loginRedirect');
    }

    /**
     * Reset user account.
     *
     * @param  Request  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function reset(Request $request)
    {
        $user = $request->user();
        $account = $user->account;

        ResetAccount::dispatch([
            'account_id' => $account->id,
        ]);

        return redirect()->route('settings.index')
                    ->with('status', trans('settings.reset_success'));
    }

    /**
     * Display the import view.
     *
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
     */
    public function import()
    {
        $accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);

        if (auth()->user()->account->importjobs->count() == 0) {
            return view('settings.imports.blank')
                ->withAccountHasLimitations($accountHasLimitations);
        }

        return view('settings.imports.index')
            ->withAccountHasLimitations($accountHasLimitations);
    }

    /**
     * Display the Import people's view.
     *
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
     */
    public function upload()
    {
        if (AccountHelper::hasLimitations(auth()->user()->account)) {
            return redirect()->route('settings.subscriptions.index');
        }

        return view('settings.imports.upload');
    }

    public function storeImport(ImportsRequest $request)
    {
        $filename = $request->file('vcard')->store('imports', config('filesystems.default'));

        $importJob = auth()->user()->account->importjobs()->create([
            'user_id' => auth()->user()->id,
            'type' => 'vcard',
            'filename' => $filename,
        ]);

        AddContactFromVCard::dispatch($importJob, $request->input('behaviour'));

        return redirect()->route('settings.import');
    }

    /**
     * Display the import report view.
     *
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
     */
    public function report($importJobId)
    {
        $importJob = ImportJob::where('account_id', auth()->user()->account_id)
            ->findOrFail($importJobId);

        return view('settings.imports.report', compact('importJob'));
    }

    /**
     * Display the users view.
     *
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
     */
    public function users()
    {
        $users = auth()->user()->account->users;
        $accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account);

        if ($users->count() == 1 && auth()->user()->account->invitations()->count() == 0) {
            return view('settings.users.blank')
                ->withAccountHasLimitations($accountHasLimitations);
        }

        return view('settings.users.index', compact('users'))
            ->withAccountHasLimitations($accountHasLimitations);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse
     */
    public function addUser()
    {
        if (AccountHelper::hasLimitations(auth()->user()->account)) {
            return redirect()->route('settings.subscriptions.index');
        }

        return view('settings.users.add');
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  InvitationRequest  $request
     * @return \Illuminate\Http\RedirectResponse
     */
    public function inviteUser(InvitationRequest $request)
    {
        // Make sure the confirmation to invite has not been bypassed
        if (! $request->input('confirmation')) {
            return redirect()->back()->withErrors(trans('settings.users_error_please_confirm'))->withInput();
        }

        // Is the email address already taken?
        $users = User::where('email', $request->only(['email']))->count();
        if ($users > 0) {
            return redirect()->back()->withErrors(trans('settings.users_error_email_already_taken'))->withInput();
        }

        // Has this user already been invited?
        $invitations = Invitation::where('email', $request->only(['email']))->count();
        if ($invitations > 0) {
            return redirect()->back()->withErrors(trans('settings.users_error_already_invited'))->withInput();
        }

        $invitation = auth()->user()->account->invitations()->create(
            $request->only([
                'email',
            ])
            + [
                'invited_by_user_id' => auth()->user()->id,
                'account_id' => auth()->user()->account_id,
                'invitation_key' => Str::random(100),
            ]
        );

        $invitation->notify((new InvitationMail())->locale(auth()->user()->locale));

        auth()->user()->account->update([
            'number_of_invitations_sent' => auth()->user()->account->number_of_invitations_sent + 1,
        ]);

        return redirect()->route('settings.users.index')
            ->with('status', trans('settings.settings_success'));
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  Invitation  $invitation
     * @return \Illuminate\Http\RedirectResponse
     */
    public function destroyInvitation(Invitation $invitation)
    {
        $invitation->delete();

        return redirect()->route('settings.users.index')
            ->with('success', trans('settings.users_invitation_deleted_confirmation_message'));
    }

    /**
     * Delete additional user account.
     *
     * @param  int  $userID
     * @return \Illuminate\Http\RedirectResponse
     */
    public function deleteAdditionalUser($userID)
    {
        $user = User::where('account_id', auth()->user()->account_id)
            ->findOrFail($userID);

        // make sure you don't delete yourself from this screen
        if ($user->id == auth()->user()->id) {
            return redirect()->route('loginRedirect');
        }

        $user->delete();

        return redirect()->route('settings.users.index')
                ->with('success', trans('settings.users_list_delete_success'));
    }

    /**
     * Display the list of tags for this account.
     */
    public function tags()
    {
        return view('settings.tags')
            ->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
    }

    /**
     * Destroy the tag.
     *
     * @param  int  $tagId
     * @return \Illuminate\Http\RedirectResponse
     */
    public function deleteTag($tagId)
    {
        app(DestroyTag::class)->execute([
            'tag_id' => $tagId,
            'account_id' => auth()->user()->account_id,
        ]);

        return redirect()->route('settings.tags.index')
                ->with('success', trans('settings.tags_list_delete_success'));
    }
    
    /**
     * Edit a tag name.
     *
     * @param Tag $tag 
     * @param Request $request
     *
     * @return \Illuminate\Http\RedirectResponse
     */
    public function editTag(Tag $tag, Request $request): RedirectResponse
    {
        app(UpdateTag::class)->execute([
            'tag_id' => $tag->id,
            'account_id' => auth()->user()->account_id,
            'name' => $request->input('name'),
        ]);

        return back()
                ->with('success', trans('settings.tags_list_edit_success'));
    }

    public function api()
    {
        return view('settings.api.index')
            ->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
    }

    public function dav()
    {
        $davroute = route('sabre.dav');
        $email = auth()->user()->email;

        return view('settings.dav.index')
                ->withDavRoute($davroute)
                ->withCardDavRoute("{$davroute}/addressbooks/{$email}/contacts")
                ->withCalDavBirthdaysRoute("{$davroute}/calendars/{$email}/birthdays")
                ->withCalDavTasksRoute("{$davroute}/calendars/{$email}/tasks")
                ->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
    }

    public function security()
    {
        $webauthnKeys = WebauthnKey::where('user_id', auth()->id())->get();

        return view('settings.security.index')
            ->with('is2FAActivated', Google2FA::isActivated())
            ->withWebauthnKeys(WebauthnKeyResource::collection($webauthnKeys))
            ->withAccountHasLimitations(AccountHelper::hasLimitations(auth()->user()->account));
    }

    /**
     * Update the default view when viewing a contact.
     * The default view can be either the life events feed or the general data
     * about the contact (notes, reminders, ...).
     * Possible values: life-events | notes.
     *
     * @param  Request  $request
     * @return string
     */
    public function updateDefaultProfileView(Request $request)
    {
        $allowedValues = ['life-events', 'notes', 'photos'];
        /** @var string */
        $view = $request->input('name');

        if (! in_array($view, $allowedValues)) {
            return 'not allowed';
        }

        auth()->user()->profile_active_tab = $view;

        if ($view == 'life-events') {
            auth()->user()->profile_new_life_event_badge_seen = true;
        }

        auth()->user()->save();

        return $view;
    }
}
