/* ============================================================
   EATME PORTÁL – AUTH SERVICE
============================================================ */


/* ============================================================
   AKTIVAČNÍ KÓD
============================================================ */

function requestActivation(email) {

  email =
    emailNorm_(
      email
    );


  const user =
    findOne_(
      CFG.SHEETS.USERS,
      function(row) {

        return (
          emailNorm_(
            row.email
          ) ===
          email
        );

      }
    );


  if (
    !user ||
    !(
      user.active === true ||
      String(
        user.active
      ).toUpperCase() ===
        'TRUE'
    )
  ) {

    /*
     * Neprozrazujeme, zda účet existuje.
     */

    return {
      ok:true
    };

  }


  const code =
    randomCode_();


  append_(
    CFG.SHEETS.LOGIN_CODES,
    {

      code_id:
        uuid_(
          'CODE'
        ),

      email:
        email,

      purpose:
        'ACTIVATE',

      code_hash:
        hashText_(
          code
        ),

      expires_at:
        new Date(
          Date.now() +
          CFG.LOGIN_CODE_MINUTES *
          60000
        ),

      used_at:
        '',

      created_at:
        now_()

    }
  );


  MailApp.sendEmail(
    {

      to:
        email,

      subject:
        CFG.APP_NAME +
        ' – aktivace účtu',

      body:
        'Váš aktivační kód je: ' +
        code +
        '\nPlatí ' +
        CFG.LOGIN_CODE_MINUTES +
        ' minut.',

      htmlBody:
        '<p>Váš aktivační kód:</p>' +
        '<p style="font-size:28px;font-weight:bold;letter-spacing:4px">' +
        code +
        '</p>' +
        '<p>Platí ' +
        CFG.LOGIN_CODE_MINUTES +
        ' minut.</p>',

      name:
        CFG.APP_NAME

    }
  );


  return {
    ok:true
  };

}



/* ============================================================
   AKTIVACE ÚČTU
============================================================ */

function activateAccount(
  email,
  code,
  password
) {

  email =
    emailNorm_(
      email
    );


  validatePassword_(
    password
  );


  const user =
    findOne_(
      CFG.SHEETS.USERS,
      function(row) {

        return (
          emailNorm_(
            row.email
          ) ===
          email
        );

      }
    );


  if (
    !user
  ) {

    throw new Error(
      'Aktivaci nelze dokončit.'
    );

  }


  const candidates =
    rows_(
      CFG.SHEETS.LOGIN_CODES
    )
      .filter(
        function(row) {

          return (
            emailNorm_(
              row.email
            ) ===
              email &&

            String(
              row.purpose
            ) ===
              'ACTIVATE' &&

            !row.used_at
          );

        }
      )
      .sort(
        function(a,b) {

          return (
            new Date(
              b.created_at
            ) -
            new Date(
              a.created_at
            )
          );

        }
      );


  const record =
    candidates[0];


  if (
    !record ||
    new Date(
      record.expires_at
    ).getTime() <
      Date.now() ||
    String(
      record.code_hash
    ) !==
      hashText_(
        code
      )
  ) {

    throw new Error(
      'Kód je neplatný nebo vypršel.'
    );

  }


  const salt =
    Utilities
      .getUuid()
      .replace(
        /-/g,
        ''
      );


  updateBy_(
    CFG.SHEETS.USERS,
    'user_id',
    user.user_id,
    {

      password_salt:
        salt,

      password_hash:
        passwordHash_(
          password,
          salt
        )

    }
  );


  updateBy_(
    CFG.SHEETS.LOGIN_CODES,
    'code_id',
    record.code_id,
    {

      used_at:
        now_()

    }
  );


  audit_(
    user.user_id,
    'ACCOUNT_ACTIVATED',
    'USER',
    user.user_id,
    '',
    ''
  );


  return login(
    email,
    password
  );

}



/* ============================================================
   ZAPOMENUTÉ HESLO – POSLAT KÓD

   Odpověď je vždy stejná. Neprozrazujeme existenci účtu.
============================================================ */

function requestPasswordReset(email) {

  email =
    emailNorm_(
      email
    );


  const genericResult =
    {
      ok:true
    };


  if (
    !email
  ) {

    return genericResult;

  }


  const user =
    findOne_(
      CFG.SHEETS.USERS,
      function(row) {

        return (
          emailNorm_(
            row.email
          ) ===
          email
        );

      }
    );


  if (
    !user ||
    !(
      user.active === true ||
      String(
        user.active
      ).toUpperCase() ===
        'TRUE'
    )
  ) {

    return genericResult;

  }


  const code =
    randomCode_();


  append_(
    CFG.SHEETS.LOGIN_CODES,
    {

      code_id:
        uuid_(
          'CODE'
        ),

      email:
        email,

      purpose:
        'RESET_PASSWORD',

      code_hash:
        hashText_(
          code
        ),

      expires_at:
        new Date(
          Date.now() +
          CFG.LOGIN_CODE_MINUTES *
          60000
        ),

      used_at:
        '',

      created_at:
        now_()

    }
  );


  MailApp.sendEmail(
    {

      to:
        email,

      subject:
        CFG.APP_NAME +
        ' – obnovení hesla',

      body:
        'Obdrželi jsme žádost o změnu hesla k účtu ' +
        CFG.APP_NAME +
        '.\n\nOvěřovací kód: ' +
        code +
        '\n\nKód platí ' +
        CFG.LOGIN_CODE_MINUTES +
        ' minut.\n\nPokud jste o změnu hesla nežádali, tento e-mail ignorujte.',

      htmlBody:
        '<p>Obdrželi jsme žádost o změnu hesla k účtu <strong>' +
        CFG.APP_NAME +
        '</strong>.</p>' +

        '<p>Ověřovací kód:</p>' +

        '<p style="font-size:28px;font-weight:bold;letter-spacing:4px">' +
        code +
        '</p>' +

        '<p>Kód platí ' +
        CFG.LOGIN_CODE_MINUTES +
        ' minut.</p>' +

        '<p style="color:#666">Pokud jste o změnu hesla nežádali, tento e-mail ignorujte.</p>',

      name:
        CFG.APP_NAME

    }
  );


  audit_(
    user.user_id,
    'PASSWORD_RESET_REQUESTED',
    'USER',
    user.user_id,
    '',
    ''
  );


  return genericResult;

}



/* ============================================================
   ZAPOMENUTÉ HESLO – NASTAVIT NOVÉ HESLO
============================================================ */

function resetPassword(
  email,
  code,
  newPassword
) {

  email =
    emailNorm_(
      email
    );


  validatePassword_(
    newPassword
  );


  const user =
    findOne_(
      CFG.SHEETS.USERS,
      function(row) {

        return (
          emailNorm_(
            row.email
          ) ===
          email
        );

      }
    );


  /*
   * Úmyslně používáme stejnou chybu pro neexistující účet
   * i neplatný kód.
   */

  if (
    !user ||
    !(
      user.active === true ||
      String(
        user.active
      ).toUpperCase() ===
        'TRUE'
    )
  ) {

    throw new Error(
      'Kód je neplatný nebo vypršel.'
    );

  }


  const candidates =
    rows_(
      CFG.SHEETS.LOGIN_CODES
    )
      .filter(
        function(row) {

          return (
            emailNorm_(
              row.email
            ) ===
              email &&

            String(
              row.purpose
            ) ===
              'RESET_PASSWORD' &&

            !row.used_at
          );

        }
      )
      .sort(
        function(a,b) {

          return (
            new Date(
              b.created_at
            ) -
            new Date(
              a.created_at
            )
          );

        }
      );


  const record =
    candidates[0];


  if (
    !record ||
    new Date(
      record.expires_at
    ).getTime() <
      Date.now() ||
    String(
      record.code_hash
    ) !==
      hashText_(
        code
      )
  ) {

    throw new Error(
      'Kód je neplatný nebo vypršel.'
    );

  }


  /*
   * Nový salt. Používáme stejný passwordHash_ jako dosud,
   * takže se nerozbije současná autentizace.
   */

  const salt =
    Utilities
      .getUuid()
      .replace(
        /-/g,
        ''
      );


  updateBy_(
    CFG.SHEETS.USERS,
    'user_id',
    user.user_id,
    {

      password_salt:
        salt,

      password_hash:
        passwordHash_(
          newPassword,
          salt
        )

    }
  );


  /*
   * Spotřebujeme všechny dosud platné RESET_PASSWORD kódy
   * pro tento e-mail, ne pouze poslední.
   */

  candidates.forEach(
    function(candidate) {

      updateBy_(
        CFG.SHEETS.LOGIN_CODES,
        'code_id',
        candidate.code_id,
        {

          used_at:
            now_()

        }
      );

    }
  );


  /*
   * Zneplatníme VŠECHNY existující sessions uživatele.
   * Po resetu se musí všechna zařízení přihlásit znovu.
   */

  rows_(
    CFG.SHEETS.SESSIONS
  )
    .filter(
      function(session) {

        return (
          String(
            session.user_id
          ) ===
          String(
            user.user_id
          )
        );

      }
    )
    .forEach(
      function(session) {

        updateBy_(
          CFG.SHEETS.SESSIONS,
          'session_id',
          session.session_id,
          {

            expires_at:
              new Date(
                0
              )

          }
        );

      }
    );


  audit_(
    user.user_id,
    'PASSWORD_RESET_COMPLETED',
    'USER',
    user.user_id,
    '',
    ''
  );


  /*
   * Informační e-mail po změně hesla.
   */

  try {

    MailApp.sendEmail(
      {

        to:
          email,

        subject:
          CFG.APP_NAME +
          ' – heslo bylo změněno',

        body:
          'Heslo k vašemu účtu ' +
          CFG.APP_NAME +
          ' bylo právě změněno.\n\nVšechny předchozí relace byly odhlášeny.\n\nPokud jste tuto změnu neprovedli vy, kontaktujte administrátora.',

        htmlBody:
          '<p>Heslo k vašemu účtu <strong>' +
          CFG.APP_NAME +
          '</strong> bylo právě změněno.</p>' +

          '<p>Všechny předchozí relace byly odhlášeny.</p>' +

          '<p><strong>Pokud jste tuto změnu neprovedli vy, kontaktujte administrátora.</strong></p>',

        name:
          CFG.APP_NAME

      }
    );

  }

  catch(error) {

    /*
     * Selhání informačního e-mailu nesmí vrátit zpět
     * už úspěšně změněné heslo.
     */

    console.error(
      'PASSWORD RESET CONFIRMATION EMAIL ERROR:',
      error
    );

  }


  return {
    ok:true
  };

}



/* ============================================================
   LOGIN
============================================================ */

function login(
  email,
  password
) {

  email =
    emailNorm_(
      email
    );


  const user =
    findOne_(
      CFG.SHEETS.USERS,
      function(row) {

        return (
          emailNorm_(
            row.email
          ) ===
          email
        );

      }
    );


  if (
    !user ||
    !user.password_salt ||
    !user.password_hash
  ) {

    throw new Error(
      'Neplatný e-mail nebo heslo.'
    );

  }


  const calculatedHash =
    passwordHash_(
      password,
      user.password_salt
    );


  if (
    calculatedHash !==
    String(
      user.password_hash
    )
  ) {

    throw new Error(
      'Neplatný e-mail nebo heslo.'
    );

  }


  let employee =
    null;


  if (
    user.employee_id
  ) {

    employee =
      findOne_(
        CFG.SHEETS.EMPLOYEES,
        function(row) {

          return (
            String(
              row.employee_id
            ) ===
            String(
              user.employee_id
            )
          );

        }
      );

  }


  const token =
    randomToken_();


  const loginTime =
    now_();


  append_(
    CFG.SHEETS.SESSIONS,
    {

      session_id:
        uuid_(
          'SES'
        ),

      user_id:
        user.user_id,

      token_hash:
        hashText_(
          token
        ),

      expires_at:
        new Date(
          Date.now() +
          CFG.SESSION_DAYS *
          86400000
        ),

      created_at:
        loginTime,

      last_seen_at:
        loginTime

    }
  );


  updateBy_(
    CFG.SHEETS.USERS,
    'user_id',
    user.user_id,
    {

      last_login:
        loginTime

    }
  );


  return {

    ok:
      true,

    token:
      token,

    role:
      user.role,

    user_id:
      user.user_id,

    user: {

      user_id:
        user.user_id,

      email:
        user.email,

      role:
        user.role,

      employee:
        employee
          ? {

              employee_id:
                employee.employee_id,

              first_name:
                employee.first_name,

              last_name:
                employee.last_name,

              company_id:
                employee.company_id,

              location_id:
                employee.location_id,

              position:
                employee.position

            }
          : null

    }

  };

}



/* ============================================================
   LOGOUT
============================================================ */

function logout(token) {

  const tokenHash =
    hashText_(
      String(
        token ||
        ''
      )
    );


  const session =
    findOne_(
      CFG.SHEETS.SESSIONS,
      function(row) {

        return (
          String(
            row.token_hash
          ) ===
          tokenHash
        );

      }
    );


  if (
    session
  ) {

    updateBy_(
      CFG.SHEETS.SESSIONS,
      'session_id',
      session.session_id,
      {

        expires_at:
          new Date(
            0
          )

      }
    );

  }


  return {
    ok:true
  };

}



/* ============================================================
   AKTUÁLNÍ UŽIVATEL
============================================================ */

function me(token) {

  const user =
    requireUser_(
      token
    );


  const employee =
    user.employee_id

      ? findOne_(
          CFG.SHEETS.EMPLOYEES,
          function(row) {

            return (
              String(
                row.employee_id
              ) ===
              String(
                user.employee_id
              )
            );

          }
        )

      : null;


  return {

    user_id:
      user.user_id,

    email:
      user.email,

    role:
      user.role,

    employee:
      employee
        ? {

            employee_id:
              employee.employee_id,

            first_name:
              employee.first_name,

            last_name:
              employee.last_name,

            company_id:
              employee.company_id,

            location_id:
              employee.location_id,

            position:
              employee.position

          }
        : null

  };

}
