/**
 * 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 {Crash, CrashLog} from './index';
import {parseCrashLog} from './crash-utils';
import fs from 'fs';
import os from 'os';
import path from 'path';
import {promisify} from 'util';

export function parseIosCrash(
  content: string,
  fallbackReason: string,
  logDate?: Date,
) {
  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] : fallbackReason;

  let date = logDate;
  if (!date) {
    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;
    date = extractedDateString ? new Date(extractedDateString) : logDate;
  }

  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 function addFileWatcherForiOSCrashLogs(
  deviceOs: string,
  serial: string,
  reportCrash: (payload: CrashLog | Crash) => void,
) {
  const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
  if (!fs.existsSync(dir)) {
    // Directory doesn't exist
    return;
  }
  return fs.watch(dir, (_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);
    promisify(fs.exists)(filepath).then((exists) => {
      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(parseCrashLog(data, deviceOs, undefined));
        }
      });
    });
  });
}
