(function() {
  var API_BASE = '/api/admin';
  var AUTH_BASE = '/api/auth';

  function headers(extra) {
    return Object.assign({
      'Accept': 'application/json',
    }, extra || {});
  }

  async function request(path, options) {
    var response = await fetch(path, Object.assign({
      credentials: 'same-origin',
      headers: headers(),
    }, options || {}));

    if (response.status === 204) {
      return null;
    }

    var payload = null;
    var text = await response.text();

    if (text) {
      try {
        payload = JSON.parse(text);
      } catch (_error) {}
    }

    if (!response.ok) {
      if (response.status === 401 && window.CrankAuth && typeof window.CrankAuth.handleUnauthorized === 'function') {
        window.CrankAuth.handleUnauthorized();
      }
      var message = payload && payload.error
        ? payload.error.message
        : payload && payload.message
          ? payload.message
          : text
            ? text
            : ('HTTP ' + response.status);
      var error = new Error(message);
      error.status = response.status;
      error.payload = payload;
      throw error;
    }

    return payload;
  }

  async function requestText(path, options) {
    var response = await fetch(path, Object.assign({
      credentials: 'same-origin',
      headers: headers(),
    }, options || {}));

    var text = await response.text();
    var payload = null;

    if (text) {
      try {
        payload = JSON.parse(text);
      } catch (_error) {}
    }

    if (!response.ok) {
      if (response.status === 401 && window.CrankAuth && typeof window.CrankAuth.handleUnauthorized === 'function') {
        window.CrankAuth.handleUnauthorized();
      }
      var message = payload && payload.error
        ? payload.error.message
        : payload && payload.message
          ? payload.message
          : text
            ? text
            : ('HTTP ' + response.status);
      var error = new Error(message);
      error.status = response.status;
      error.payload = payload;
      throw error;
    }

    return text;
  }

  function get(path) {
    return request(API_BASE + path);
  }

  function post(path, body) {
    return request(API_BASE + path, {
      method: 'POST',
      headers: headers({ 'Content-Type': 'application/json' }),
      body: JSON.stringify(body),
    });
  }

  function patch(path, body) {
    return request(API_BASE + path, {
      method: 'PATCH',
      headers: headers({ 'Content-Type': 'application/json' }),
      body: JSON.stringify(body),
    });
  }

  function del(path) {
    return request(API_BASE + path, { method: 'DELETE' });
  }

  function query(params) {
    var search = new URLSearchParams();
    Object.keys(params || {}).forEach(function(key) {
      var value = params[key];
      if (value === undefined || value === null || value === '') {
        return;
      }
      search.set(key, value);
    });
    var encoded = search.toString();
    return encoded ? ('?' + encoded) : '';
  }

  function postBytes(path, bytes, fileName) {
    return request(API_BASE + path, {
      method: 'POST',
      headers: headers(fileName ? { 'X-File-Name': fileName } : {}),
      body: bytes,
    });
  }

  window.CrankApi = {
    login: function(payload) {
      return request(AUTH_BASE + '/login', {
        method: 'POST',
        headers: headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify(payload),
      });
    },
    logout: function() {
      return request(AUTH_BASE + '/logout', {
        method: 'POST',
        headers: headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify({}),
      });
    },
    getSession: function() {
      return request(AUTH_BASE + '/session');
    },
    getProfile: function() {
      return request(AUTH_BASE + '/profile');
    },
    updateProfile: function(payload) {
      return request(AUTH_BASE + '/profile', {
        method: 'PATCH',
        headers: headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify(payload),
      });
    },
    changePassword: function(payload) {
      return request(AUTH_BASE + '/password', {
        method: 'POST',
        headers: headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify(payload),
      });
    },
    setCurrentWorkspace: function(workspaceId) {
      return request(AUTH_BASE + '/current-workspace', {
        method: 'POST',
        headers: headers({ 'Content-Type': 'application/json' }),
        body: JSON.stringify({ workspace_id: workspaceId }),
      });
    },
    listWorkspaces: function() {
      return get('/workspaces');
    },
    getCapabilities: function() {
      return get('/capabilities');
    },
    createWorkspace: function(payload) {
      return post('/workspaces', payload);
    },
    getWorkspace: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId));
    },
    updateWorkspace: function(workspaceId, payload) {
      return patch('/workspaces/' + encodeURIComponent(workspaceId), payload);
    },
    listMemberships: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/members');
    },
    updateMembership: function(workspaceId, userId, payload) {
      return patch('/workspaces/' + encodeURIComponent(workspaceId) + '/members/' + encodeURIComponent(userId), payload);
    },
    deleteMembership: function(workspaceId, userId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/members/' + encodeURIComponent(userId));
    },
    listInvitations: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations');
    },
    createInvitation: function(workspaceId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations', payload);
    },
    deleteInvitation: function(workspaceId, invitationId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/invitations/' + encodeURIComponent(invitationId));
    },
    exportWorkspace: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/export');
    },
    deleteWorkspace: function(workspaceId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId));
    },
    listOperations: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/operations');
    },
    getOperation: function(workspaceId, operationId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId));
    },
    getOperationVersion: function(workspaceId, operationId, version) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/versions/' + encodeURIComponent(version));
    },
    createOperation: function(workspaceId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations', payload);
    },
    updateOperation: function(workspaceId, operationId, payload) {
      return patch('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId), payload);
    },
    deleteOperation: function(workspaceId, operationId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId));
    },
    archiveOperation: function(workspaceId, operationId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/archive', {});
    },
    publishOperation: function(workspaceId, operationId, version) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/publish', {
        version: version,
      });
    },
    createOperationVersion: function(workspaceId, operationId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/versions', payload);
    },
    runOperationTest: function(workspaceId, operationId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/test-runs', payload);
    },
    uploadInputSample: function(workspaceId, operationId, sample) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/samples/input-json', sample);
    },
    uploadOutputSample: function(workspaceId, operationId, sample) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/samples/output-json', sample);
    },
    uploadProtoFile: function(workspaceId, operationId, content, fileName) {
      return postBytes(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/proto',
        content,
        fileName
      );
    },
    uploadDescriptorSet: function(workspaceId, operationId, content, fileName) {
      return postBytes(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/descriptor-set',
        content,
        fileName
      );
    },
    uploadWsdlFile: function(workspaceId, operationId, content, fileName) {
      return postBytes(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/wsdl',
        content,
        fileName
      );
    },
    uploadXsdFile: function(workspaceId, operationId, content, fileName) {
      return postBytes(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/descriptors/xsd',
        content,
        fileName
      );
    },
    listGrpcServices: function(workspaceId, operationId, version) {
      return get(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/grpc/services'
        + query({ version: version })
      );
    },
    listSoapServices: function(workspaceId, operationId, version) {
      return get(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/soap/services'
        + query({ version: version })
      );
    },
    generateDraft: function(workspaceId, operationId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/drafts/generate', payload || {});
    },
    exportOperation: function(workspaceId, operationId, params) {
      return requestText(
        API_BASE + '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/' + encodeURIComponent(operationId) + '/export' + query(params),
        {
          headers: headers({ 'Accept': 'application/yaml' }),
        }
      );
    },
    importOperation: function(workspaceId, yamlDocument, mode) {
      return request(
        API_BASE + '/workspaces/' + encodeURIComponent(workspaceId) + '/operations/import' + query({ mode: mode }),
        {
          method: 'POST',
          headers: headers({ 'Content-Type': 'application/yaml' }),
          body: yamlDocument,
        }
      );
    },
    listAgents: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/agents');
    },
    createAgent: function(workspaceId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents', payload);
    },
    getAgent: function(workspaceId, agentId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId));
    },
    updateAgent: function(workspaceId, agentId, payload) {
      return patch('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId), payload);
    },
    deleteAgent: function(workspaceId, agentId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId));
    },
    saveAgentBindings: function(workspaceId, agentId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/bindings', payload);
    },
    publishAgent: function(workspaceId, agentId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/publish', payload);
    },
    unpublishAgent: function(workspaceId, agentId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/unpublish', {});
    },
    archiveAgent: function(workspaceId, agentId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/archive', {});
    },
    listAgentPlatformApiKeys: function(workspaceId, agentId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/platform-api-keys');
    },
    createAgentPlatformApiKey: function(workspaceId, agentId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/platform-api-keys', payload);
    },
    revokeAgentPlatformApiKey: function(workspaceId, agentId, keyId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/platform-api-keys/' + encodeURIComponent(keyId) + '/revoke', {});
    },
    deleteAgentPlatformApiKey: function(workspaceId, agentId, keyId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/agents/' + encodeURIComponent(agentId) + '/platform-api-keys/' + encodeURIComponent(keyId));
    },
    listSecrets: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/secrets');
    },
    createSecret: function(workspaceId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/secrets', payload);
    },
    getSecret: function(workspaceId, secretId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/secrets/' + encodeURIComponent(secretId));
    },
    rotateSecret: function(workspaceId, secretId, payload) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/secrets/' + encodeURIComponent(secretId) + '/rotate', payload);
    },
    deleteSecret: function(workspaceId, secretId) {
      return del('/workspaces/' + encodeURIComponent(workspaceId) + '/secrets/' + encodeURIComponent(secretId));
    },
    listAuthProfiles: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/auth-profiles');
    },
    listLogs: function(workspaceId, params) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/logs' + query(params));
    },
    getLog: function(workspaceId, logId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/logs/' + encodeURIComponent(logId));
    },
    getUsageOverview: function(workspaceId, params) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/usage' + query(params));
    },
    getProtocolCapabilities: function(workspaceId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/protocol-capabilities');
    },
    listStreamSessions: function(workspaceId, params) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/stream-sessions' + query(params));
    },
    getStreamSession: function(workspaceId, sessionId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/stream-sessions/' + encodeURIComponent(sessionId));
    },
    stopStreamSession: function(workspaceId, sessionId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/stream-sessions/' + encodeURIComponent(sessionId) + '/stop', {});
    },
    listAsyncJobs: function(workspaceId, params) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/async-jobs' + query(params));
    },
    getAsyncJob: function(workspaceId, jobId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/async-jobs/' + encodeURIComponent(jobId));
    },
    cancelAsyncJob: function(workspaceId, jobId) {
      return post('/workspaces/' + encodeURIComponent(workspaceId) + '/async-jobs/' + encodeURIComponent(jobId) + '/cancel', {});
    },
    getAsyncJobResult: function(workspaceId, jobId) {
      return get('/workspaces/' + encodeURIComponent(workspaceId) + '/async-jobs/' + encodeURIComponent(jobId) + '/result');
    },
    getOperationUsage: function(workspaceId, operationId, params) {
      return get(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/usage/operations/' + encodeURIComponent(operationId) + query(params)
      );
    },
    getAgentUsage: function(workspaceId, agentId, params) {
      return get(
        '/workspaces/' + encodeURIComponent(workspaceId) + '/usage/agents/' + encodeURIComponent(agentId) + query(params)
      );
    },
  };

}());
