<?php

namespace App\Http\Livewire;

use App\Models\User;
use App\Rules\CorrectTwoFactorCode;
use App\Services\Log;
use chillerlan\QRCode\QRCode;
use Exception;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rule;
use Livewire\Component;
use Livewire\WithPagination;
use PHPGangsta_GoogleAuthenticator;
use Schema;

class Users extends Component {

	use WithPagination;

	protected string $paginationTheme = 'bootstrap';

	public $filterSearch, $deleteId, $userId, $name, $email, $password, $confirmPassword, $role, $twoFactorSecret, $twoFactorCurrentSecret, $twoFactorCode, $twoFactorQrCode, $requireTwoFactor;

	protected $listeners = [ 'updateItem', 'filterItems' ];

	public function getCreateRules(): array {
		return [
			'name'            => 'required',
			'email'           => 'required|email',
			'password'        => 'required|string|min:8',
			'confirmPassword' => 'required|same:password',
			'role'            => [ 'required', Rule::in( array_keys( User::getRoles() ) ) ],
			'twoFactorCode'   => [ 'required_if:requireTwoFactor,true', new CorrectTwoFactorCode( $this->requireTwoFactor, $this->twoFactorSecret, $this->twoFactorCode ) ]
		];
	}

	public function getUpdateRules(): array {
		return [
			'name'            => 'required',
			'email'           => 'required|email',
			'password'        => 'sometimes|string|min:8',
			'confirmPassword' => 'sometimes|required_with:password|same:password',
			'role'            => [ 'required', Rule::in( array_keys( User::getRoles() ) ) ],
			'twoFactorCode'   => [
				Rule::requiredIf( $this->requireTwoFactor && $this->twoFactorSecret != $this->twoFactorCurrentSecret ),
				new CorrectTwoFactorCode( $this->requireTwoFactor, $this->twoFactorSecret, $this->twoFactorCode, $this->twoFactorCurrentSecret )
			]
		];
	}

	public function getMessages(): array {
		return [
			'twoFactorCode.required_if' => __( 'Two factor code is required.' )
		];
	}

	public function createItemUpdated( $propertyName ): void {
		if ( $propertyName == 'password' && $this->confirmPassword != '' ) {
			$this->validateOnly( 'confirmPassword', $this->getCreateRules() );
		}

		if ( $propertyName == 'twoFactorSecret' ) {
			$data                  = 'otpauth://totp/' . getOption( 'ui', 'brand', 'Keevault' ) . '?secret=' . $this->twoFactorSecret;
			$this->twoFactorQrCode = ( new QRCode() )->render( $data );
		}

		$this->validateOnly( $propertyName, $this->getCreateRules() );
	}

	public function updateItemUpdated( $propertyName ): void {
		if ( $propertyName == 'password' && $this->confirmPassword != '' ) {
			$this->validateOnly( 'confirmPassword', $this->getUpdateRules() );
		}

		if ( $propertyName == 'twoFactorSecret' && $this->twoFactorSecret != '' ) {
			$data                  = 'otpauth://totp/' . getOption( 'ui', 'brand', 'Keevault' ) . '?secret=' . $this->twoFactorSecret;
			$this->twoFactorQrCode = ( new QRCode() )->render( $data );
		}

		if ( $this->twoFactorSecret == '' ) {
			$this->twoFactorQrCode = '';
		}

		$this->validateOnly( $propertyName, $this->getUpdateRules() );
	}

	public function createItem(): void {
		$this->validate( $this->getCreateRules() );

		$user                    = new User();
		$user->name              = $this->name;
		$user->email             = $this->email;
		$user->password          = Hash::make( $this->password );
		$user->role              = $this->role;
		$user->is_2fa_enabled    = $this->requireTwoFactor;
		$user->two_factor_secret = '';

		if ( $this->requireTwoFactor ) {
			$ga = new PHPGangsta_GoogleAuthenticator();

			if ( $ga->verifyCode( $this->twoFactorSecret, $this->twoFactorCode, 2 ) ) {
				$user->is_2fa_enabled    = true;
				$user->two_factor_secret = $this->twoFactorSecret;
			}
		}

		$user->save();

		Log::add( '140', Auth::id(), request()->ip(), [ 'user-id' => $user->id ] );

		$this->dispatchBrowserEvent( 'close-create-modal' );
	}


	public function updateItem(): void {
		if ( \Config::get( 'app.is_demo', false ) ) {
			$this->dispatchBrowserEvent( 'show-toast', [
				'toastType'    => 'danger',
				'toastMessage' => __( 'This action is disabled in the demo!' )
			] );

			return;
		}

		$this->validate( $this->getUpdateRules() );

		$user                 = User::find( $this->userId );
		$user->name           = $this->name;
		$user->email          = $this->email;
		$user->role           = $this->role;
		$user->is_2fa_enabled = $this->requireTwoFactor;

		if ( $this->password != '' ) {
			$user->password = Hash::make( $this->password );
		}

		if ( $user->two_factor_secret != $this->twoFactorSecret ) {
			if ( $this->requireTwoFactor ) {
				$ga = new PHPGangsta_GoogleAuthenticator();

				if ( $ga->verifyCode( $this->twoFactorSecret, $this->twoFactorCode, 2 ) ) {
					$user->is_2fa_enabled    = true;
					$user->two_factor_secret = $this->twoFactorSecret;
				}
			}
		}

		$user->save();

		Log::add( '141', Auth::id(), request()->ip(), [ 'user-id' => $user->id ] );

		$this->dispatchBrowserEvent( 'close-update-modal' );
	}

	public function deleteItem(): void {
		if ( \Config::get( 'app.is_demo', false ) ) {
			$this->dispatchBrowserEvent( 'show-toast', [
				'toastType'    => 'danger',
				'toastMessage' => __( 'This action is disabled in the demo!' )
			] );

			return;
		}

		if ( Auth::id() == $this->deleteId ) {
			$this->dispatchBrowserEvent( 'show-toast', [
				'toastType'    => 'danger',
				'toastMessage' => __( 'This action is not allowed.' )
			] );

			return;
		}

		User::where( 'id', $this->deleteId )->delete();

		Log::add( '142', Auth::id(), request()->ip(), [ 'user-id' => $this->deleteId ] );

		$this->dispatchBrowserEvent( 'close-delete-modal' );
	}

	public function openCreateModal(): void {
		$this->resetInputs();

		$this->dispatchBrowserEvent( 'open-create-modal' );
	}

	public function openUpdateModal( $id ): void {
		$this->resetInputs();

		$user                         = User::find( $id );
		$this->userId                 = $user->id;
		$this->name                   = $user->name;
		$this->email                  = $user->email;
		$this->role                   = $user->role;
		$this->requireTwoFactor       = $user->is_2fa_enabled;
		$this->twoFactorSecret        = $user->two_factor_secret;
		$this->twoFactorCurrentSecret = $user->two_factor_secret;

		if ( $user->two_factor_secret == '' ) {
			$this->twoFactorQrCode = '';
		}


		$this->dispatchBrowserEvent( 'open-update-modal' );
	}

	public function resetInputs(): void {
		$this->deleteId         = 0;
		$this->userId           = 0;
		$this->name             = '';
		$this->email            = '';
		$this->password         = '';
		$this->confirmPassword  = '';
		$this->role             = '';
		$this->twoFactorCode    = '';
		$this->requireTwoFactor = false;
		$this->twoFactorQrCode  = '';

		$ga = new PHPGangsta_GoogleAuthenticator();

		try {
			$this->twoFactorSecret = $ga->createSecret();
		} catch ( Exception $e ) {
			$this->twoFactorSecret = '';
		}

		if ( $this->twoFactorSecret != '' ) {
			$data                  = 'otpauth://totp/' . getOption( 'ui', 'brand', 'Keevault' ) . '?secret=' . $this->twoFactorSecret;
			$this->twoFactorQrCode = ( new QRCode() )->render( $data );
		}

		$this->resetValidation();
	}

	public function filterItems(): void {
		$this->dispatchBrowserEvent( 'filter-done' );
	}

	public function render() {
		return view( 'livewire.users.manage', [
			'users' => User::where( function ( $q ) {
				if ( $this->filterSearch != '' ) {
					foreach ( Schema::getColumnListing( 'users' ) as $column ) {
						$q->orWhere( $column, 'like', '%' . $this->filterSearch . '%' );
					}
				}
			} )->orderBy( 'id', 'DESC' )->paginate( getOption( 'ui', 'itemsPerTable', 10 ) ),
			'roles' => User::getRoles()
		] );
	}
}
