<?php

namespace Dolphy\Plugin\Security;

use Com\Tecnick\Barcode\Barcode;
use Com\Tecnick\Barcode\Exception as BarcodeException;
use Dolphy\Plugin\Exceptions\Exception;
use Dolphy\Plugin\Helpers\Crypto;
use Dolphy\Plugin\Helpers\DB;
use Exception as GlobalException;
use PHPGangsta_GoogleAuthenticator;
use WP_User;

require_once DOLPHY_VENDOR.'/PHPGangsta/GoogleAuthenticator.php';

final class TwoFA {

  final public static function table() {
    return DB::tbl('2fa');
  }

  public static function generateNewInfo(WP_User $user) {
    $ga = new PHPGangsta_GoogleAuthenticator();

    $info['secret'] = $ga->createSecret(32);
    $info['qrCode'] = self::qrcode($user->display_name, $info['secret']);
    $info['backup'] = TwoFABackup::genarate(6);

    return $info;
  }

  private static function qrcode($name, $secret) {
    $issuer = get_bloginfo('name', 'display');
    $name = urldecode("{$issuer}: {$name}");

    $gdAvailable = extension_loaded('gd') && function_exists('gd_info');
    $imagickAvailable = extension_loaded('imagick') || class_exists('Imagick');

    if (!$gdAvailable && !$imagickAvailable) {
      $ga = new PHPGangsta_GoogleAuthenticator();
      return $ga->getQRCodeGoogleUrl($name, $secret, $issuer);
    } else {
      $data = "otpauth://totp/{$name}?secret={$secret}&issuer={$issuer}";

      $barcode = new Barcode();
      $obj = $barcode->getBarcodeObj('QRCODE,H', $data, -16, -16, '#000000', array(5, 5, 5, 5));
      $obj = $obj->setBackgroundColor('#FFF');

      $pngData = $obj->getPngData($imagickAvailable);
      $pngData = base64_encode($pngData);

      return "data:image/png;base64,{$pngData}";
    }
  }

  public static function findBy($field, $value) {
    global $wpdb;
    $table = self::table();
    $query = $wpdb->prepare("SELECT * FROM `{$table}` WHERE `{$field}` = %s", $value);
    return $wpdb->get_row($query);
  }

  public static function get($userId) {
    return self::findBy('userId', $userId);
  }

  public static function verify(WP_User $user, $code, $dplc = NULL) {
    if (!($data = self::get($user->ID))) {
      throw new Exception('Two-Factor Authentication not enabled.', 400);
    }

    $secret = Crypto::decrypt($data->secret);

    return self::verifyWithSecret($secret, $code, $dplc);
  }

  public static function verifyWithSecret($secret, $code, $dplc = NULL) {
    $ga = new PHPGangsta_GoogleAuthenticator();
    $verified = $ga->verifyCode($secret, $code);

    if ($verified === true && !empty($dplc)) {
      DPLCVerified::add('2fa', $dplc);
    }

    return $verified;
  }

  public static function enable(WP_User $user, $secret, $code, array $backup) {
    global $wpdb;

    if (!self::verifyWithSecret($secret, $code)) {
      throw new Exception('Invalid Authentication Code', 400);
    }

    $wpdb->insert(self::table(), array(
      'userId' => $user->ID,
      'secret' => Crypto::encrypt($secret)
    ));

    if ($wpdb->insert_id === false) {
      throw new Exception('Unable to enable Two-Factor Authentication. Please try again later');
    }

    TwoFABackup::save($user->ID, $backup);

    return self::findBy('id', $wpdb->insert_id);
  }

  public static function disable($userId) {
    global $wpdb;

    if (!($data = self::findBy('userId', $userId))) {
      throw new Exception('2FA not enabled', 400);
    }

    $wpdb->delete(self::table(), array(
      'userId' => $userId
    ));

    TwoFABackup::clear($userId);

    return $data;
  }

  private static function cookieName($userId) {
    return "dolphy-2fa-user-{$userId}";
  }

  public static function remember($userId) {
    $name = self::cookieName($userId);

    $url = parse_url(home_url());
    $domain = $_SERVER['HTTP_HOST'];
    $path = (isset($url['path'])) ? $url['path'] : '/';

    setcookie($name, "1", (time() + (60*60*24*30)), $path, $domain, is_ssl(), true);
  }

  public static function isRemembered($userId) {
    $name = self::cookieName($userId);
    return isset($_COOKIE[$name]) && $_COOKIE[$name] === '1';
  }

}
