import { IFetchAuth } from '../../../services/types';
import { IAuthenticate, IJwtCredentialV2 } from './types';
import { objectAssign } from '../../../utils/lang/objectAssign';
import { encodeToBase64 } from '../../../utils/base64';
import { decodeJWTtoken } from '../../../utils/jwt';
import { hash } from '../../../utils/murmur3/murmur3';

/**
 * Factory of authentication function.
 *
 * @param fetchAuth - `ServiceApi.fetchAuth` endpoint
 */
export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {

  /**
   * Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
   * @param userKeys - set of user Keys to track membership updates. It is undefined for server-side API.
   */
  return function authenticate(userKeys?: string[]): Promise<IJwtCredentialV2> {
    return fetchAuth(userKeys)
      .then(resp => resp.json())
      .then(json => {
        if (json.token) { // empty token when `"pushEnabled": false`
          const decodedToken = decodeJWTtoken(json.token);
          if (typeof decodedToken.iat !== 'number' || typeof decodedToken.exp !== 'number') throw new Error('token properties "issuedAt" (iat) or "expiration" (exp) are missing or invalid');
          const channels = JSON.parse(decodedToken['x-ably-capability']);
          return objectAssign({
            decodedToken,
            channels
          }, json);
        }
        return json;
      });
  };
}

/**
 * Returns the hash of a given user key
 */
export function hashUserKey(userKey: string): string {
  return encodeToBase64(hash(userKey, 0).toString());
}
