import * as URL from 'url';

export interface IOAuthAction {
  readonly name: 'oauth';
  readonly code: string;
}

export interface IOpenRepositoryFromURLAction {
  readonly name: 'open-repository-from-url';

  /** the remote repository location associated with the "Open in Desktop" action */
  readonly url: string;

  /** the optional branch name which should be checked out. use the default branch otherwise. */
  readonly branch: string | null;

  /** the pull request number, if pull request originates from a fork of the repository */
  readonly pr: string | null;

  /** the file to open after cloning the repository */
  readonly filepath: string | null;
}

export interface IOpenRepositoryFromPathAction {
  readonly name: 'open-repository-from-path';

  /** The local path to open. */
  readonly path: string;
}

export interface IUnknownAction {
  readonly name: 'unknown';
  readonly url: string;
}

export type URLActionType =
  | IOAuthAction
  | IOpenRepositoryFromURLAction
  | IOpenRepositoryFromPathAction
  | IUnknownAction;

// eslint-disable-next-line typescript/interface-name-prefix
interface ParsedUrlQueryWithUndefined {
  // `undefined` is added here to ensure we handle the missing querystring key
  // See https://github.com/Microsoft/TypeScript/issues/13778 for discussion about
  // why this isn't supported natively in TypeScript
  [key: string]: string | string[] | undefined;
}

/**
 * Parse the URL to find a given key in the querystring text.
 *
 * @param url The source URL containing querystring key-value pairs
 * @param key The key to look for in the querystring
 */
function getQueryStringValue(
  query: ParsedUrlQueryWithUndefined,
  key: string
): string | null {
  const value = query[key];
  if (value == null) {
    return null;
  }

  if (Array.isArray(value)) {
    return value[0];
  }

  return value;
}

export function parseAppURL(url: string): URLActionType {
  const parsedURL = URL.parse(url, true);
  const hostname = parsedURL.hostname;
  const unknown: IUnknownAction = { name: 'unknown', url };
  if (!hostname) {
    return unknown;
  }

  const query = parsedURL.query;

  const actionName = hostname.toLowerCase();
  if (actionName === 'oauth') {
    const code = getQueryStringValue(query, 'code');
    if (code != null) {
      return { name: 'oauth', code };
    } else {
      return unknown;
    }
  }

  // we require something resembling a URL first
  // - bail out if it's not defined
  // - bail out if you only have `/`
  const pathName = parsedURL.pathname;
  if (!pathName || pathName.length <= 1) {
    return unknown;
  }

  // Trim the trailing / from the URL
  const parsedPath = pathName.substr(1);

  if (actionName === 'openrepo') {
    const probablyAURL = parsedPath;

    // suffix the remote URL with `.git`, for backwards compatibility
    const _url = `${probablyAURL}.git`;

    const pr = getQueryStringValue(query, 'pr');
    const branch = getQueryStringValue(query, 'branch');
    const filepath = getQueryStringValue(query, 'filepath');

    if (pr != null) {
      if (!/^\d+$/.test(pr)) {
        return unknown;
      }

      // we also expect the branch for a forked PR to be a given ref format
      if (branch != null && !/^pr\/\d+$/.test(branch)) {
        return unknown;
      }
    }

    // if (branch != null && testForInvalidChars(branch)) {
    //   return unknown;
    // }

    return {
      name: 'open-repository-from-url',
      url: _url,
      branch,
      pr,
      filepath,
    };
  }

  if (actionName === 'openlocalrepo') {
    return {
      name: 'open-repository-from-path',
      path: decodeURIComponent(parsedPath),
    };
  }

  return unknown;
}
