<?php

namespace admin\factory;

class Users
{
  public static function user_delete($user_id)
  {
    global $mdb;

    return $mdb->delete('pp_users', ['id' => (int)$user_id]);
  }

  public static function user_details($user_id)
  {
    global $mdb;
    return $mdb->get('pp_users', '*', ['id' => (int)$user_id]);
  }

  public static function user_privileges($user_id)
  {
    global $mdb;
    return $mdb->select('pp_users_privileges', '*', ['id_user' => (int)$user_id]);
  }

  public static function user_save($user_id, $login, $status, $active_to, $password, $password_re, $admin, $privileges, $twofa_enabled = 0, $twofa_email = '' )
  {
    global $mdb, $lang;

    $mdb->delete('pp_users_privileges', ['id_user' => (int) $user_id]);

    if (!$user_id)
    {
      if (strlen($password) < 5)
        return $response = ['status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.'];

      if ($password != $password_re)
        return $response = ['status' => 'error', 'msg' => 'Podane hasła są różne'];

      if ($mdb->insert(
        'pp_users',
        [
          'login'     => $login,
          'status'    => $status == 'on' ? 1 : 0,
          'active_to' => $active_to == '' ? NULL : $active_to,
          'admin'     => $admin,
          'password'  => md5($password),
          'twofa_enabled' => $twofa_enabled == 'on' ? 1 : 0,
          'twofa_email'   => $twofa_email
        ]
      ))
        $id_user = $mdb->get('pp_users', 'id', ['ORDER' => ['id' => 'DESC']]);

      if (is_array($privileges))
      {
        foreach ($privileges as $pri)
        {
          $mdb->insert(
            'pp_users_privileges',
            [
              'name'    => $pri,
              'id_user' => $id_user
            ]
          );
        }
      }
      else
      {
        $mdb->insert(
          'pp_users_privileges',
          [
            'name'    => $privileges,
            'id_user' => $id_user
          ]
        );
      }

      return $response = ['status' => 'ok', 'msg' => 'Użytkownik został zapisany.'];
    }
    else
    {

      if ($password and strlen($password) < 5)
        return $response = ['status' => 'error', 'msg' => 'Podane hasło jest zbyt krótkie.'];

      if ($password and $password != $password_re)
        return $response = ['status' => 'error', 'msg' => 'Podane hasła są różne'];

      if ($password)
        $mdb->update('pp_users', [
          'password' => md5($password)
        ], [
          'id' => (int) $user_id
        ]);

      $mdb->update('pp_users', [
        'login'              => $login,
        'admin'              => $admin,
        'status'             => $status == 'on' ? 1 : 0,
        'active_to'          => $active_to == '' ? NULL : $active_to,
        'error_logged_count' => 0,
        'twofa_enabled'      => $twofa_enabled == 'on' ? 1 : 0,
        'twofa_email'        => $twofa_email
      ], [
        'id' => (int) $user_id
      ]);

      if (is_array($privileges))
      {
        foreach ($privileges as $pri)
        {
          $mdb->insert('pp_users_privileges', [
            'name'    => $pri,
            'id_user' => $user_id
          ]);
        }
      }
      else
      {
        $mdb->insert('pp_users_privileges', [
          'name'    => $privileges,
          'id_user' => $user_id
        ]);
      }
      return $response = ['status' => 'ok', 'msg' => 'Uzytkownik został zapisany.'];
    }
    \S::delete_cache();
  }

  public static function check_login($login, $user_id)
  {
    global $mdb;

    if ($mdb->get('pp_users', 'login', ['AND' => ['login' => $login, 'id[!]' => (int)$user_id]]))
      return $response = ['status' => 'error', 'msg' => 'Podany login jest już zajęty.'];

    return $response = ['status' => 'ok'];
  }

  public static function logon($login, $password)
  {
    global $mdb;

    if (!$mdb->get('pp_users', '*', ['login' => $login]))
      return 0;

    if (!$mdb->get('pp_users', '*', ['AND' => ['login' => $login, 'status' => 1, 'error_logged_count[<]' => 5]]))
      return -1;

    if ($mdb->get('pp_users', '*', [
      'AND' => [
        'login' => $login,
        'status' => 1,
        'password' => md5($password),
        'OR' => ['active_to[>=]' => date('Y-m-d'), 'active_to' => null]
      ]
    ]))
    {
      $mdb->update('pp_users', ['last_logged' => date('Y-m-d H:i:s'), 'error_logged_count' => 0], ['login' => $login]);
      return 1;
    }
    else
    {
      $mdb->update('pp_users', ['last_error_logged' => date('Y-m-d H:i:s'), 'error_logged_count[+]' => 1], ['login' => $login]);
      if ($mdb->get('pp_users', 'error_logged_count', ['login' => $login]) >= 5)
      {
        $mdb->update('pp_users', ['status' => 0], ['login' => $login]);
        return -1;
      }
    }
    return 0;
  }

  public static function details($login)
  {
    global $mdb;
    return $mdb->get('pp_users', '*', ['login' => $login]);
  }

  public static function check_privileges($name, $user_id)
  {
    global $mdb;

    if ($user_id == 1)
      return true;
    else
    {
      if (!$privilages = \Cache::fetch("check_privileges:$user_id:$name-tmp"))
      {
        $privilages = $mdb->count('pp_users_privileges', ['AND' => ['name' => $name, 'id_user' => (int)$user_id]]);
        \Cache::store("check_privileges:$user_id:$name", $privilages);
      }
      return $privilages;
    }
  }

  static public function get_by_id(int $userId): ?array
  {

    global $mdb;
    return $mdb->get('pp_users', '*', ['id' => $userId]) ?: null;
  }

  static public function send_twofa_code(int $userId, bool $resend = false): bool
  {

    $user = self::get_by_id($userId);
    if (!$user)
      return false;

    if ((int)$user['twofa_enabled'] !== 1)
    {
      return false;
    }

    $to = $user['twofa_email'] ?: $user['login'];
    if (!filter_var($to, FILTER_VALIDATE_EMAIL))
    {
      return false;
    }

    if ($resend && !empty($user['twofa_sent_at']))
    {
      $last = strtotime($user['twofa_sent_at']);
      if ($last && (time() - $last) < 30)
      {
        return false;
      }
    }

    $code = random_int(100000, 999999);
    $hash = password_hash((string)$code, PASSWORD_DEFAULT);

    self::update_by_id($userId, [
      'twofa_code_hash'       => $hash,
      'twofa_expires_at'      => date('Y-m-d H:i:s', time() + 10 * 60), // 10 minut
      'twofa_sent_at'         => date('Y-m-d H:i:s'),
      'twofa_failed_attempts' => 0,
    ]);

    $subject = 'Twój kod logowania 2FA';
    $body = "Twój kod logowania do panelu administratora: {$code}. Kod jest ważny przez 10 minut. Jeśli to nie Ty inicjowałeś logowanie – zignoruj tę wiadomość i poinformuj administratora.";

    $sent = \S::send_email($to, $subject, $body);

    if (!$sent) {
        $headers  = "MIME-Version: 1.0\r\n";
        $headers .= "Content-type: text/plain; charset=UTF-8\r\n";
        $headers .= "From: no-reply@" . ($_SERVER['HTTP_HOST'] ?? 'localhost') . "\r\n";
        $encodedSubject = mb_encode_mimeheader($subject, 'UTF-8');

        $sent = mail($to, $encodedSubject, $body, $headers);
    }

    return $sent;
  }

  static public function update_by_id(int $userId, array $data): bool
  {
    global $mdb;
    return (bool)$mdb->update('pp_users', $data, ['id' => $userId]);
  }

  static public function verify_twofa_code(int $userId, string $code): bool
  {
    $user = self::get_by_id( $userId );
    if (!$user) return false;

    if ((int)$user['twofa_failed_attempts'] >= 5)
    {
      return false; // zbyt wiele prób
    }

    // sprawdź ważność
    if (empty($user['twofa_expires_at']) || time() > strtotime($user['twofa_expires_at']))
    {
      // wyczyść po wygaśnięciu
      self::update_by_id($userId, [
        'twofa_code_hash'      => null,
        'twofa_expires_at'     => null,
      ]);
      return false;
    }

    $ok = (!empty($user['twofa_code_hash']) && password_verify($code, $user['twofa_code_hash']));
    if ($ok)
    {
      // sukces: czyścimy wszystko
      self::update_by_id($userId, [
        'twofa_code_hash'       => null,
        'twofa_expires_at'      => null,
        'twofa_sent_at'         => null,
        'twofa_failed_attempts' => 0,
        'last_logged'           => date('Y-m-d H:i:s'),
      ]);
      return true;
    }

    // zła próba — inkrementacja
    self::update_by_id($userId, [
      'twofa_failed_attempts' => (int)$user['twofa_failed_attempts'] + 1,
      'last_error_logged'     => date('Y-m-d H:i:s'),
    ]);
    return false;
  }
}
