/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 */

import type {CrashLog} from './index';
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
import {UNKNOWN_CRASH_REASON} from './crash-utils';

export function parseIosCrash(content: string) {
  const regex = /Exception Type: *\w*/;
  const arr = regex.exec(content);
  const exceptionString = arr ? arr[0] : '';
  const exceptionRegex = /\w*$/;
  const tmp = exceptionRegex.exec(exceptionString);
  const exception = tmp && tmp[0].length ? tmp[0] : UNKNOWN_CRASH_REASON;

  const dateRegex = /Date\/Time: *[\w\s\.:-]*/;
  const dateArr = dateRegex.exec(content);
  const dateString = dateArr ? dateArr[0] : '';
  const dateRegex2 = /[\w\s\.:-]*$/;
  const tmp1 = dateRegex2.exec(dateString);
  const extractedDateString: string | null =
    tmp1 && tmp1[0].length ? tmp1[0] : null;
  const date = extractedDateString
    ? new Date(extractedDateString).getTime()
    : Date.now();

  const crash: CrashLog = {
    callstack: content,
    name: exception,
    reason: exception,
    date,
  };
  return crash;
}

export function shouldShowiOSCrashNotification(
  serial: string,
  content: string,
): boolean {
  const appPath = parsePath(content);
  if (!appPath || !appPath.includes(serial)) {
    // Do not show notifications for the app which are not running on this device
    return false;
  }
  return true;
}

export function parsePath(content: string): string | null {
  const regex = /(?<=.*Path: *)[^\n]*/;
  const arr = regex.exec(content);
  if (!arr || arr.length <= 0) {
    return null;
  }
  const path = arr[0];
  return path.trim();
}

export async function addFileWatcherForiOSCrashLogs(
  serial: string,
  reportCrash: (payload: CrashLog) => void,
) {
  const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
  if (!(await fs.pathExists(dir))) {
    // Directory doesn't exist
    return;
  }
  return fs.watch(dir, async (_eventType, filename) => {
    // We just parse the crash logs with extension `.crash`
    const checkFileExtension = /.crash$/.exec(filename);
    if (!filename || !checkFileExtension) {
      return;
    }
    const filepath = path.join(dir, filename);
    const exists = await fs.pathExists(filepath);
    if (!exists) {
      return;
    }
    fs.readFile(filepath, 'utf8', function (err, data) {
      if (err) {
        console.warn('Failed to read crash file', err);
        return;
      }
      if (shouldShowiOSCrashNotification(serial, data)) {
        reportCrash(parseIosCrash(data));
      }
    });
  });
}
