<?php
namespace Controllers;

class UsersController
{
  private const ADMIN_USER_ID = 1;
  private const IMPERSONATOR_SESSION_KEY = 'impersonator_user';

  public static function mainView()
  {
    global $user, $mdb;

    if ( !$user )
      return \controls\Users::login_form();

    $impersonator_user = self::getImpersonatorUser();
    if ( !self::canManageUsers( $user, $impersonator_user ) )
      self::forbiddenRedirect();

    $users_repository = new \Domain\Users\UserRepository();
    $all_users = $users_repository -> all();

    $permission_repo = new \Domain\Users\PermissionRepository( $mdb );
    $permissions_map = [];
    foreach ( $all_users as $u )
    {
      if ( (int)$u['id'] !== self::ADMIN_USER_ID )
        $permissions_map[ (int)$u['id'] ] = $permission_repo -> byUserId( (int)$u['id'] );
    }

    return \Tpl::view( 'users/main-view', self::buildMainViewModel(
      $user,
      $impersonator_user,
      $all_users,
      $permissions_map
    ) );
  }

  public static function loginAs()
  {
    global $user;

    if ( !$user )
      return \controls\Users::login_form();

    $impersonator_user = self::getImpersonatorUser();
    if ( !self::canManageUsers( $user, $impersonator_user ) )
      self::forbiddenRedirect();

    $target_user_id = (int)\S::get( 'user_id' );
    $users_repository = new \Domain\Users\UserRepository();
    $target_user = $users_repository -> byId( $target_user_id );

    if ( !$target_user )
    {
      \S::alert( 'Nie znaleziono wskazanego uzytkownika.' );
      header( 'Location: /users/main_view/' );
      exit;
    }

    $new_session_state = self::impersonationStateAfterLoginAs( $user, $target_user, $impersonator_user );

    \S::set_session( 'user', $new_session_state['user'] );
    \S::set_session( self::IMPERSONATOR_SESSION_KEY, $new_session_state['impersonator_user'] );

    \S::alert( 'Zalogowano jako: ' . $target_user['name'] . ' ' . $target_user['surname'] . '.' );
    header( 'Location: /' );
    exit;
  }

  public static function switchBackToAdmin()
  {
    $impersonator_user = self::getImpersonatorUser();

    if ( !$impersonator_user or !isset( $impersonator_user['id'] ) or (int)$impersonator_user['id'] !== self::ADMIN_USER_ID )
    {
      \S::alert( 'Brak aktywnej sesji podszywania.' );
      header( 'Location: /' );
      exit;
    }

    \S::set_session( 'user', $impersonator_user );
    \S::del_session( self::IMPERSONATOR_SESSION_KEY );

    \S::alert( 'Powrot do konta administratora.' );
    header( 'Location: /users/main_view/' );
    exit;
  }

  public static function canManageUsers( $current_user, $impersonator_user = null )
  {
    if ( !is_array( $current_user ) )
      return false;

    if ( isset( $current_user['id'] ) and (int)$current_user['id'] === self::ADMIN_USER_ID )
      return true;

    if ( is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === self::ADMIN_USER_ID )
      return true;

    return false;
  }

  public static function vacations()
  {
    global $user, $mdb;

    if ( !$user )
      return \controls\Users::login_form();

    $impersonator_user = self::getImpersonatorUser();
    if ( !self::canManageUsers( $user, $impersonator_user ) )
      self::forbiddenRedirect();

    $users_repository = new \Domain\Users\UserRepository();
    $vacation_repository = new \Domain\Users\VacationRepository( $mdb );
    $all_users = $users_repository -> all();

    $year = (int) \S::get( 'year' ) ?: (int) date( 'Y' );
    $filter_user_id = (int) \S::get( 'user_id' ) ?: 0;

    $vacations = $vacation_repository -> allByYear( $year, $filter_user_id ?: null );
    $summary = $vacation_repository -> summaryByYear( $year, $all_users );
    $carryover = $vacation_repository -> carryoverByYear( (int) date( 'Y' ), $all_users );

    return \Tpl::view( 'users/vacations', [
      'active_tab' => 'vacations',
      'can_switch_back' => is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === self::ADMIN_USER_ID,
      'users' => $all_users,
      'vacations' => $vacations,
      'summary' => $summary,
      'carryover' => $carryover,
      'year' => $year,
      'filter_user_id' => $filter_user_id,
      'vacation_types' => \Domain\Users\VacationRepository::TYPES
    ] );
  }

  public static function vacationAdd()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystąpił błąd podczas dodawania urlopu.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnień.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidłowy token bezpieczeństwa. Odśwież stronę.';
      echo json_encode( $response );
      exit;
    }

    $user_id = (int) \S::get( 'user_id' );
    $date_from = \S::get( 'date_from' );
    $date_to = \S::get( 'date_to' );
    $type = \S::get( 'type' );
    $comment = \S::get( 'comment' );

    if ( !$user_id || !$date_from || !$date_to || !$type )
    {
      $response['msg'] = 'Wypełnij wszystkie wymagane pola.';
      echo json_encode( $response );
      exit;
    }

    if ( !preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_from ) || !preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_to ) )
    {
      $response['msg'] = 'Nieprawidłowy format daty.';
      echo json_encode( $response );
      exit;
    }

    $vacation_repository = new \Domain\Users\VacationRepository( $mdb );
    $id = $vacation_repository -> add( $user_id, $date_from, $date_to, $type, $comment );

    if ( $id )
      $response = [ 'status' => 'success', 'msg' => 'Urlop został dodany.' ];

    echo json_encode( $response );
    exit;
  }

  public static function vacationEdit()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystąpił błąd podczas edycji urlopu.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnień.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidłowy token bezpieczeństwa. Odśwież stronę.';
      echo json_encode( $response );
      exit;
    }

    $id = (int) \S::get( 'id' );
    $user_id = (int) \S::get( 'user_id' );
    $date_from = \S::get( 'date_from' );
    $date_to = \S::get( 'date_to' );
    $type = \S::get( 'type' );
    $comment = \S::get( 'comment' );

    if ( !$id || !$user_id || !$date_from || !$date_to || !$type )
    {
      $response['msg'] = 'Wypełnij wszystkie wymagane pola.';
      echo json_encode( $response );
      exit;
    }

    if ( !preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_from ) || !preg_match( '/^\d{4}-\d{2}-\d{2}$/', $date_to ) )
    {
      $response['msg'] = 'Nieprawidłowy format daty.';
      echo json_encode( $response );
      exit;
    }

    $vacation_repository = new \Domain\Users\VacationRepository( $mdb );

    if ( $vacation_repository -> update( $id, $user_id, $date_from, $date_to, $type, $comment ) )
      $response = [ 'status' => 'success', 'msg' => 'Urlop został zaktualizowany.' ];

    echo json_encode( $response );
    exit;
  }

  public static function vacationDelete()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystąpił błąd podczas usuwania urlopu.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnień.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidłowy token bezpieczeństwa. Odśwież stronę.';
      echo json_encode( $response );
      exit;
    }

    $id = (int) \S::get( 'id' );

    if ( !$id )
    {
      echo json_encode( $response );
      exit;
    }

    $vacation_repository = new \Domain\Users\VacationRepository( $mdb );

    if ( $vacation_repository -> delete( $id ) )
      $response = [ 'status' => 'success', 'msg' => 'Urlop został usunięty.' ];

    echo json_encode( $response );
    exit;
  }

  public static function vacationLimitSave()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystąpił błąd podczas zapisywania limitu.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnień.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidłowy token bezpieczeństwa. Odśwież stronę.';
      echo json_encode( $response );
      exit;
    }

    $user_id = (int) \S::get( 'user_id' );
    $year = (int) \S::get( 'year' );
    $days_limit = (int) \S::get( 'days_limit' );

    if ( !$user_id || !$year || $days_limit < 0 )
    {
      echo json_encode( $response );
      exit;
    }

    $vacation_repository = new \Domain\Users\VacationRepository( $mdb );

    if ( $vacation_repository -> setLimit( $user_id, $year, $days_limit ) )
      $response = [ 'status' => 'success', 'msg' => 'Limit został zapisany.' ];

    echo json_encode( $response );
    exit;
  }

  public static function permissionSave()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystapil blad podczas zapisywania uprawnien.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnien.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidlowy token bezpieczenstwa. Odswiez strone.';
      echo json_encode( $response );
      exit;
    }

    $target_user_id = (int) \S::get( 'user_id' );
    $module = \S::get( 'perm_module' );
    $value = (int) \S::get( 'value' );

    if ( !$target_user_id || !$module )
    {
      echo json_encode( $response );
      exit;
    }

    // Superadmin permissions cannot be changed
    if ( $target_user_id === self::ADMIN_USER_ID )
    {
      $response['msg'] = 'Nie mozna zmieniac uprawnien administratora.';
      echo json_encode( $response );
      exit;
    }

    // Whitelist check
    if ( !in_array( $module, \Domain\Users\PermissionRepository::MODULES, true ) )
    {
      $response['msg'] = 'Nieznany modul.';
      echo json_encode( $response );
      exit;
    }

    $repo = new \Domain\Users\PermissionRepository( $mdb );
    $current = $repo -> byUserId( $target_user_id );
    $current[ $module ] = $value ? 1 : 0;
    $repo -> save( $target_user_id, $current );

    $response = [ 'status' => 'success', 'msg' => 'Uprawnienia zostaly zapisane.' ];
    echo json_encode( $response );
    exit;
  }

  public static function permissionPopup()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Nie mozna otworzyc ustawien uprawnien.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnien.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidlowy token bezpieczenstwa. Odswiez strone.';
      echo json_encode( $response );
      exit;
    }

    $target_user_id = (int)\S::get( 'user_id' );
    if ( !$target_user_id )
    {
      echo json_encode( $response );
      exit;
    }

    $users_repository = new \Domain\Users\UserRepository();
    $target_user = $users_repository -> byId( $target_user_id );
    if ( !$target_user )
    {
      $response['msg'] = 'Nie znaleziono uzytkownika.';
      echo json_encode( $response );
      exit;
    }

    $permission_repo = new \Domain\Users\PermissionRepository( $mdb );
    $permissions = (int)$target_user['id'] === self::ADMIN_USER_ID
      ? \Domain\Users\PermissionRepository::defaults()
      : $permission_repo -> byUserId( (int)$target_user['id'] );

    $defs = self::permissionDefinitions();

    $response = [
      'status' => 'success',
      'popup_content' => \Tpl::view( 'users/permissions-popup', [
        'target_user' => $target_user,
        'permissions' => $permissions,
        'module_labels' => $defs['module_labels'],
        'permission_groups' => $defs['permission_groups']
      ] )
    ];

    echo json_encode( $response );
    exit;
  }

  public static function permissionSaveBulk()
  {
    global $user, $mdb;
    header( 'Content-Type: application/json; charset=utf-8' );

    $response = [ 'status' => 'error', 'msg' => 'Wystapil blad podczas zapisywania uprawnien.' ];

    if ( !$user || !self::canManageUsers( $user, self::getImpersonatorUser() ) )
    {
      $response['msg'] = 'Brak uprawnien.';
      echo json_encode( $response );
      exit;
    }

    if ( !\S::csrf_verify() )
    {
      $response['msg'] = 'Nieprawidlowy token bezpieczenstwa. Odswiez strone.';
      echo json_encode( $response );
      exit;
    }

    $target_user_id = (int)\S::get( 'user_id' );
    $selected_modules_raw = (string)\S::get( 'selected_modules' );

    if ( !$target_user_id )
    {
      echo json_encode( $response );
      exit;
    }

    if ( $target_user_id === self::ADMIN_USER_ID )
    {
      $response['msg'] = 'Nie mozna zmieniac uprawnien administratora.';
      echo json_encode( $response );
      exit;
    }

    $selected_modules = array_filter( array_map( 'trim', explode( ',', $selected_modules_raw ) ) );
    $selected_modules = array_values( array_unique( $selected_modules ) );

    $payload = [];
    foreach ( \Domain\Users\PermissionRepository::MODULES as $module )
      $payload[ $module ] = in_array( $module, $selected_modules, true ) ? 1 : 0;

    $repo = new \Domain\Users\PermissionRepository( $mdb );
    $repo -> save( $target_user_id, $payload );

    echo json_encode( [ 'status' => 'success', 'msg' => 'Uprawnienia zostaly zapisane.' ] );
    exit;
  }

  public static function buildMainViewModel( $current_user, $impersonator_user, array $users, array $permissions_map = [] )
  {
    $defs = self::permissionDefinitions();

    return [
      'current_user' => $current_user,
      'impersonator_user' => $impersonator_user,
      'users' => $users,
      'active_tab' => 'users',
      'can_switch_back' => is_array( $impersonator_user ) and isset( $impersonator_user['id'] ) and (int)$impersonator_user['id'] === self::ADMIN_USER_ID,
      'permissions_map' => $permissions_map,
      'modules' => \Domain\Users\PermissionRepository::MODULES,
      'module_labels' => $defs['module_labels'],
      'permission_groups' => $defs['permission_groups']
    ];
  }

  public static function impersonationStateAfterLoginAs( $current_user, $target_user, $existing_impersonator_user = null )
  {
    $impersonator_user = $existing_impersonator_user;

    if ( !is_array( $impersonator_user ) )
      $impersonator_user = ( is_array( $current_user ) and isset( $current_user['id'] ) and (int)$current_user['id'] === self::ADMIN_USER_ID ) ? $current_user : null;

    return [
      'user' => $target_user,
      'impersonator_user' => $impersonator_user
    ];
  }

  private static function getImpersonatorUser()
  {
    $session_value = \S::get_session( self::IMPERSONATOR_SESSION_KEY );

    if ( is_array( $session_value ) )
      return $session_value;

    return null;
  }

  private static function forbiddenRedirect()
  {
    \S::alert( 'Brak uprawnien do zarzadzania uzytkownikami.' );
    header( 'Location: /' );
    exit;
  }

  private static function permissionDefinitions()
  {
    return [
      'module_labels' => [
        'tasks' => 'Zadania',
        'projects_view' => 'Projekty: przegladanie',
        'projects_add' => 'Projekty: dodawanie',
        'projects_edit' => 'Projekty: edycja',
        'projects_delete' => 'Projekty: usuwanie',
        'work_time' => 'Czas pracy',
        'finances' => 'Finanse',
        'crm' => 'CRM',
        'wiki' => 'Wiki'
      ],
      'permission_groups' => [
        'Podstawowe' => [ 'tasks', 'work_time', 'wiki' ],
        'Projekty' => [ 'projects_view', 'projects_add', 'projects_edit', 'projects_delete' ],
        'Pozostale' => [ 'finances', 'crm' ]
      ]
    ];
  }
}
