/*
 * Copyright 2019 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

import { SecureServerOptions } from 'http2';
import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers';

export interface KeyCertPair {
  private_key: Buffer;
  cert_chain: Buffer;
}

export abstract class ServerCredentials {
  abstract _isSecure(): boolean;
  abstract _getSettings(): SecureServerOptions | null;

  static createInsecure(): ServerCredentials {
    return new InsecureServerCredentials();
  }

  static createSsl(
    rootCerts: Buffer | null,
    keyCertPairs: KeyCertPair[],
    checkClientCertificate = false
  ): ServerCredentials {
    if (rootCerts !== null && !Buffer.isBuffer(rootCerts)) {
      throw new TypeError('rootCerts must be null or a Buffer');
    }

    if (!Array.isArray(keyCertPairs)) {
      throw new TypeError('keyCertPairs must be an array');
    }

    if (typeof checkClientCertificate !== 'boolean') {
      throw new TypeError('checkClientCertificate must be a boolean');
    }

    const cert = [];
    const key = [];

    for (let i = 0; i < keyCertPairs.length; i++) {
      const pair = keyCertPairs[i];

      if (pair === null || typeof pair !== 'object') {
        throw new TypeError(`keyCertPair[${i}] must be an object`);
      }

      if (!Buffer.isBuffer(pair.private_key)) {
        throw new TypeError(`keyCertPair[${i}].private_key must be a Buffer`);
      }

      if (!Buffer.isBuffer(pair.cert_chain)) {
        throw new TypeError(`keyCertPair[${i}].cert_chain must be a Buffer`);
      }

      cert.push(pair.cert_chain);
      key.push(pair.private_key);
    }

    return new SecureServerCredentials({
      ca: rootCerts || getDefaultRootsData() || undefined,
      cert,
      key,
      requestCert: checkClientCertificate,
      ciphers: CIPHER_SUITES,
    });
  }
}

class InsecureServerCredentials extends ServerCredentials {
  _isSecure(): boolean {
    return false;
  }

  _getSettings(): null {
    return null;
  }
}

class SecureServerCredentials extends ServerCredentials {
  private options: SecureServerOptions;

  constructor(options: SecureServerOptions) {
    super();
    this.options = options;
  }

  _isSecure(): boolean {
    return true;
  }

  _getSettings(): SecureServerOptions {
    return this.options;
  }
}
