// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-glyph: images; icon-color: cyan; share-sheet-inputs: file-url,url,image,plain-text;
/**
 *
 * @version 1.1.0
 * @author Honye
 */

/**
 * @param {object} options
 * @param {string} [options.title]
 * @param {string} [options.message]
 * @param {Array<{ title: string; [key: string]: any }>} options.options
 * @param {boolean} [options.showCancel = true]
 * @param {string} [options.cancelText = 'Cancel']
 */
const presentSheet = async (options) => {
  options = {
    showCancel: true,
    cancelText: 'Cancel',
    ...options
  };
  const alert = new Alert();
  if (options.title) {
    alert.title = options.title;
  }
  if (options.message) {
    alert.message = options.message;
  }
  if (!options.options) {
    throw new Error('The "options" property of the parameter cannot be empty')
  }
  for (const option of options.options) {
    alert.addAction(option.title);
  }
  if (options.showCancel) {
    alert.addCancelAction(options.cancelText);
  }
  const value = await alert.presentSheet();
  return {
    value,
    option: options.options[value]
  }
};

/**
 * 多语言国际化
 * @param {{[language: string]: string} | [en:string, zh:string]} langs
 */
const i18n = (langs) => {
  const language = Device.language();
  if (Array.isArray(langs)) {
    langs = {
      en: langs[0],
      zh: langs[1],
      others: langs[0]
    };
  } else {
    langs.others = langs.others || langs.en;
  }
  return langs[language] || langs.others
};

// Variables used by Scriptable.

const ALERTS_AS_SHEETS = false;
const fm = FileManager.local();
const CACHE_FOLDER = 'cache/nobg';
const cachePath = fm.joinPath(fm.documentsDirectory(), CACHE_FOLDER);
const deviceId = `${Device.model()}_${Device.name()}`.replace(/[^a-zA-Z0-9\-_]/, '').toLowerCase();

const generateSlices = async function ({ caller = 'none' }) {
  const opts = { caller };

  const appearance = (await isUsingDarkAppearance()) ? 'dark' : 'light';
  const altAppearance = appearance === 'dark' ? 'light' : 'dark';

  if (!fm.fileExists(cachePath)) {
    fm.createDirectory(cachePath, true);
  }

  let message;

  message = i18n([
    'To change background make sure you have a screenshot of you home screen. Go to your home screen and enter wiggle mode. Scroll to the empty page on the far right and take a screenshot.',
    '设置背景前先确认已有主屏截图。返回主屏打开编辑模式,滑动到最右边后截图'
  ]);
  const options = [
    i18n(['Pick Screenshot', '选择截图']),
    i18n(['Exit to Take Screenshot', '退出去截屏'])
  ];
  let resp = await presentAlert(message, options, ALERTS_AS_SHEETS);
  if (resp === 1) return false

  // Get screenshot and determine phone size.
  const wallpaper = await Photos.fromLibrary();
  const height = wallpaper.size.height;
  let suffix = '';

  // Check for iPhone 12 Mini here:
  if (height === 2436) {
    // We'll save everything in the config, to keep things centralized
    const cfg = await loadConfig();
    if (cfg['phone-model'] === undefined) {
      // Doesn't exist, ask them which phone they want to generate for,
      // the mini or the others?
      message = i18n(['What model of iPhone do you have?', '确认手机机型']);
      const options = ['iPhone 12 mini', 'iPhone 11 Pro, XS, or X'];
      resp = await presentAlert(message, options, ALERTS_AS_SHEETS);
      // 0 represents iPhone Mini and 1 others.
      cfg['phone-model'] = resp;
      await saveConfig(cfg); // Save the config
      if (resp === 0) {
        suffix = '_mini';
      }
    } else {
      // Config already contains iPhone model, use it from cfg
      if (cfg['phone-model']) {
        suffix = '_mini';
      }
    }
  }

  const phone = phoneSizes[height + suffix];
  if (!phone) {
    message = i18n([
      "It looks like you selected an image that isn't an iPhone screenshot, or your iPhone is not supported. Try again with a different image.",
      '看似你选的图不是屏幕截图或不支持你的机型,尝试使用其他截图'
    ]);
    await presentAlert(message, [i18n(['OK', '好的'])], ALERTS_AS_SHEETS);
    return false
  }

  /** @type {('small'|'medium'|'large')[]} */
  const families = ['small', 'medium', 'large'];

  // generate crop rects for all sizes
  for (let i = 0; i < families.length; i++) {
    const widgetSize = families[i];

    const crops = widgetPositions[widgetSize].map(item => {
      const position = item.value;

      const crop = { pos: position, w: 0, h: 0, x: 0, y: 0 };
      crop.w = phone[widgetSize].w;
      crop.h = phone[widgetSize].h;
      crop.x = phone.left;

      const pos = position.split('-');

      crop.y = phone[pos[0]];

      if (widgetSize === 'large' && pos[0] === 'bottom') {
        crop.y = phone.middle;
      }

      if (pos.length > 1) {
        crop.x = phone[pos[1]];
      }

      return crop
    });

    for (let c = 0; c < crops.length; c++) {
      const crop = crops[c];
      const imgCrop = cropImage(wallpaper, new Rect(crop.x, crop.y, crop.w, crop.h));

      const imgName = `${deviceId}-${appearance}-${widgetSize}-${crop.pos}.jpg`;
      const imgPath = fm.joinPath(cachePath, imgName);

      if (fm.fileExists(imgPath)) {
        try { fm.remove(imgPath); } catch (e) { }
      }
      fm.writeImage(imgPath, imgCrop);
    }
  }

  if (opts.caller !== 'self') {
    message = i18n([
      `Slices saved for ${appearance} mode. You can switch to ${altAppearance} mode and run this again to also generate slices.`,
      `已经保存${appearance === 'dark' ? '夜间' : '白天'}透明背景。你可以切换到${altAppearance === 'dark' ? '夜间' : '白天'}模式后再次运行`
    ]);
  } else {
    message = i18n(['Slices saved.', '已保存']);
  }
  await presentAlert(message, [i18n(['Ok', '好的'])], ALERTS_AS_SHEETS);

  return true
};
// ------------------------------------------------
/**
 * @param {string} instanceName
 * @param {boolean} reset
 */
const getSliceForWidget = async function (instanceName, reset = false) {
  const appearance = (await isUsingDarkAppearance()) ? 'dark' : 'light';
  let position = reset ? null : await getConfig(instanceName);
  if (!position) {
    log(`Background for "${instanceName}" is not yet set.`);

    // check if slices exists
    const testImage = fm.joinPath(cachePath, `${deviceId}-${appearance}-medium-top.jpg`);
    let readyToChoose = false;
    if (!fm.fileExists(testImage)) {
      // need to generate slices
      // FIXME
      readyToChoose = await generateSlices({ caller: instanceName || 'self' });
    } else {
      readyToChoose = true;
    }

    // now set the
    let backgrounChosen;
    if (readyToChoose) {
      backgrounChosen = await chooseBackgroundSlice(instanceName);
    }

    if (backgrounChosen) {
      const cfg = await loadConfig();
      position = cfg[instanceName];
    } else {
      return null
    }
  }
  const imgPath = fm.joinPath(cachePath, `${deviceId}-${appearance}-${position}.jpg`);
  if (!fm.fileExists(imgPath)) {
    log(`Slice does not exists - ${deviceId}-${appearance}-${position}.jpg`);
    return null
  }

  const image = fm.readImage(imgPath);
  return image
};
// ------------------------------------------------
const transparent = getSliceForWidget;
// ------------------------------------------------
const chooseBackgroundSlice = async function (name) {
  // Prompt for widget size and position.
  let message = i18n(['What is the size of the widget?', '组件尺寸']);
  /** @type {{label:string;value:'small'|'medium'|'large'}[]} */
  const sizes = [
    { label: i18n(['Small', '小号']), value: 'small' },
    { label: i18n(['Medium', '中号']), value: 'medium' },
    { label: i18n(['Large', '大号']), value: 'large' },
    { label: i18n(['Cancel', '取消']), value: 'cancel' }
  ];
  const size = await presentAlert(message, sizes, ALERTS_AS_SHEETS);
  if (size === 3) return false
  const widgetSize = sizes[size].value;

  message = i18n(['Where will it be placed on?', '组件位置']);
  const positions = widgetPositions[widgetSize];
  positions.push(i18n(['Cancel', '取消']));
  const resp = await presentAlert(message, positions, ALERTS_AS_SHEETS);

  if (resp === positions.length - 1) return false
  const position = positions[resp].value;

  const cfg = await loadConfig();
  cfg[name] = `${widgetSize}-${position}`;

  await saveConfig(cfg);
  await presentAlert(i18n(['Background saved.', '已保存']), [i18n(['Ok', '好'])], ALERTS_AS_SHEETS);
  return true
};
/**
 * @param {string} instance
 * @returns {Promise<string|undefined>}
 */
const getConfig = async (instance) => {
  try {
    const conf = await loadConfig();
    return conf[instance]
  } catch (err) {}
};

// -- [helpers] -----------------------------------
// ------------------------------------------------
/**
 * @returns {Promise<Record<string, string>>}
 */
async function loadConfig () {
  const configPath = fm.joinPath(cachePath, 'widget-positions.json');
  if (!fm.fileExists(configPath)) {
    await fm.writeString(configPath, '{}');
    return {}
  } else {
    const strConf = fm.readString(configPath);
    const cfg = JSON.parse(strConf);
    return cfg
  }
}

const hasConfig = async () => {
  try {
    const conf = await loadConfig();
    return conf
  } catch (err) {
    return false
  }
};
// ------------------------------------------------
/**
 * @param {Record<string, string>} cfg
 */
async function saveConfig (cfg) {
  const configPath = fm.joinPath(cachePath, 'widget-positions.json');
  await fm.writeString(configPath, JSON.stringify(cfg));
  return cfg
}
// ------------------------------------------------
async function presentAlert (
  prompt = '',
  items = ['OK'],
  asSheet = false
) {
  const alert = new Alert();
  alert.message = prompt;

  for (let n = 0; n < items.length; n++) {
    const item = items[n];
    if (typeof item === 'string') {
      alert.addAction(item);
    } else {
      alert.addAction(item.label);
    }
  }
  const resp = asSheet
    ? await alert.presentSheet()
    : await alert.presentAlert();
  return resp
}
// ------------------------------------------------
/**
 * @type {Record<'small'|'medium'|'large', {label:string;value:string}[]>}
 */
const widgetPositions = {
  small: [
    { label: i18n(['Top Left', '左上']), value: 'top-left' },
    { label: i18n(['Top Right', '右上']), value: 'top-right' },
    { label: i18n(['Middle Left', '左中']), value: 'middle-left' },
    { label: i18n(['Middle Right', '右中']), value: 'middle-right' },
    { label: i18n(['Bottom Left', '左下']), value: 'bottom-left' },
    { label: i18n(['Bottom Right', '右下']), value: 'bottom-right' }
  ],
  medium: [
    { label: i18n(['Top', '上方']), value: 'top' },
    { label: i18n(['Middle', '中部']), value: 'middle' },
    { label: i18n(['Bottom', '下方']), value: 'bottom' }
  ],
  large: [
    { label: i18n(['Top', '上方']), value: 'top' },
    { label: i18n(['Bottom', '下方']), value: 'bottom' }
  ]
};
// ------------------------------------------------
/**
 * @type {Record<string|number, {
 * models: string[];
 * small: { w: number; h: number };
 * medium: { w: number; h: number };
 * large: { w: number; h: number };
 * left:number; right:number; top:number; middle:number; bottom:number;
 * }>}
 */
const phoneSizes = {
  2796: {
    models: ['14 Pro Max'],
    small: { w: 510, h: 510 },
    medium: { w: 1092, h: 510 },
    large: { w: 1092, h: 1146 },
    left: 99,
    right: 681,
    top: 282,
    middle: 918,
    bottom: 1554
  },

  2556: {
    models: ['14 Pro'],
    small: { w: 474, h: 474 },
    medium: { w: 1014, h: 474 },
    large: { w: 1014, h: 1062 },
    left: 82,
    right: 622,
    top: 270,
    middle: 858,
    bottom: 1446
  },

  2778: {
    models: ['12 Pro Max', '13 Pro Max', '14 Plus'],
    small: { w: 510, h: 510 },
    medium: { w: 1092, h: 510 },
    large: { w: 1092, h: 1146 },
    left: 96,
    right: 678,
    top: 246,
    middle: 882,
    bottom: 1518
  },

  2532: {
    models: ['12', '12 Pro', '13', '14'],
    small: { w: 474, h: 474 },
    medium: { w: 1014, h: 474 },
    large: { w: 1014, h: 1062 },
    left: 78,
    right: 618,
    top: 231,
    middle: 819,
    bottom: 1407
  },

  2688: {
    models: ['Xs Max', '11 Pro Max'],
    small: { w: 507, h: 507 },
    medium: { w: 1080, h: 507 },
    large: { w: 1080, h: 1137 },
    left: 81,
    right: 654,
    top: 228,
    middle: 858,
    bottom: 1488
  },

  1792: {
    models: ['11', 'Xr'],
    small: { w: 338, h: 338 },
    medium: { w: 720, h: 338 },
    large: { w: 720, h: 758 },
    left: 54,
    right: 436,
    top: 160,
    middle: 580,
    bottom: 1000
  },

  2436: {
    models: ['X', 'Xs', '11 Pro'],
    small: { w: 465, h: 465 },
    medium: { w: 987, h: 465 },
    large: { w: 987, h: 1035 },
    left: 69,
    right: 591,
    top: 213,
    middle: 783,
    bottom: 1353
  },

  '2436_mini': {
    models: ['12 Mini'],
    small: { w: 465, h: 465 },
    medium: { w: 987, h: 465 },
    large: { w: 987, h: 1035 },
    left: 69,
    right: 591,
    top: 231,
    middle: 801,
    bottom: 1371
  },

  2208: {
    models: ['6+', '6s+', '7+', '8+'],
    small: { w: 471, h: 471 },
    medium: { w: 1044, h: 471 },
    large: { w: 1044, h: 1071 },
    left: 99,
    right: 672,
    top: 114,
    middle: 696,
    bottom: 1278
  },

  1334: {
    models: ['6', '6s', '7', '8'],
    small: { w: 296, h: 296 },
    medium: { w: 642, h: 296 },
    large: { w: 642, h: 648 },
    left: 54,
    right: 400,
    top: 60,
    middle: 412,
    bottom: 764
  },

  1136: {
    models: ['5', '5s', '5c', 'SE'],
    small: { w: 282, h: 282 },
    medium: { w: 584, h: 282 },
    large: { w: 584, h: 622 },
    left: 30,
    right: 332,
    top: 59,
    middle: 399,
    bottom: 399
  }
};
// ------------------------------------------------
function cropImage (img, rect) {
  const draw = new DrawContext();
  draw.size = new Size(rect.width, rect.height);
  draw.drawImageAtPoint(img, new Point(-rect.x, -rect.y));
  return draw.getImage()
}
// ------------------------------------------------
async function isUsingDarkAppearance () {
  return !(Color.dynamic(Color.white(), Color.black()).red)
}

const localFile = FileManager.local();
const APP_ROOT = localFile.joinPath(localFile.documentsDirectory(), Script.name());
const PHOTOS_DIR = localFile.joinPath(APP_ROOT, 'photos');

const main = async () => {
  if (!localFile.fileExists(PHOTOS_DIR)) {
    localFile.createDirectory(PHOTOS_DIR, true);
  }

  if (config.runsInActionExtension) {
    choosePhotos();
    return
  }

  if (config.runsInApp) {
    const {
      option: { key } = {}
    } = await presentSheet({
      options: [
        {
          title: i18n(['Preview', '预览']),
          key: 'preview'
        },
        {
          title: i18n(['Photos', '查看图片']),
          key: 'photos'
        },
        {
          title: i18n(['Transparent background', '透明背景']),
          key: 'transparentBg'
        }
      ],
      cancelText: i18n(['Cancel', '取消'])
    });
    if (key === 'preview') {
      const widget = await createWidget();
      widget.presentSmall();
      Script.complete();
      return
    }
    if (key === 'photos') {
      presentAlbums();
      return
    }
    if (key === 'transparentBg') {
      if (await hasConfig()) {
        const { option } = await presentSheet({
          options: [
            {
              title: i18n(['Update widget size and position', '修改组件尺寸和位置']),
              value: 'update'
            },
            {
              title: i18n(['Update wallpaper screenshot', '更新壁纸截图']),
              value: 'reset'
            }
          ],
          cancelText: i18n(['Cancel', '取消'])
        });
        if (option) {
          if (option.value === 'update') {
            await transparent(Script.name(), true);
          } else {
            await generateSlices({ caller: Script.name() });
          }
        }
      } else {
        await transparent(Script.name(), true);
      }
      return
    }
  }

  if (config.runsInWidget) {
    const widget = await createWidget();
    Script.setWidget(widget);
    Script.complete();
  }
};

/** 通过分享菜单选择照片 */
const choosePhotos = async () => {
  const albums = getAlbums();
  let album;
  const { option } = await presentSheet({
    message: i18n([
      'Choose Album',
      '选择相册'
    ]),
    options: [
      ...albums.map((name) => ({ title: name, type: 'album' })),
      {
        title: i18n(['New Album', '新建相册']),
        type: 'new'
      }
    ]
  });
  if (option) {
    if (option.type === 'album') {
      album = option.title;
    }
    if (option.type === 'new') {
      album = await createAlbum();
    }
  }
  const albumDir = localFile.joinPath(PHOTOS_DIR, album);

  const filePaths = args.fileURLs;
  const images = args.images;
  if (filePaths && filePaths.length) { // 图片文件分享
    for (const filePath of filePaths) {
      const filename = localFile.fileName(filePath, true);
      const copyPath = localFile.joinPath(albumDir, filename);
      try {
        localFile.copy(filePath, copyPath);
      } catch (e) {
        await alert(e.message);
      }
    }
  } else if (images && images.length) { // 图片分享
    for (const image of images) {
      const filePath = localFile.joinPath(albumDir, `${Date.now()}.jpg`);
      localFile.writeImage(filePath, image);
    }
  }

  presentPhotos(album);
};

/**
 * @param {string} album
 */
const _choosePhoto = async (album) => {
  const {
    option: { key } = {}
  } = await presentSheet({
    options: [
      {
        title: i18n(['Camera', '拍照']),
        key: 'camera'
      },
      {
        title: i18n(['Albums', '相册']),
        key: 'album'
      }
    ]
  });
  const image = await (async () => {
    if (key === 'camera') {
      return await Photos.fromCamera()
    }
    if (key === 'album') {
      return await Photos.fromLibrary()
    }
  })();
  const filename = `${Date.now().toString()}.jpg`;
  const albumDir = localFile.joinPath(PHOTOS_DIR, album);
  const filePath = localFile.joinPath(albumDir, filename);
  localFile.writeImage(filePath, image);
  return filePath
};

const getAlbums = () => {
  const albums = localFile.listContents(PHOTOS_DIR)
    .filter((name) => localFile.isDirectory(localFile.joinPath(PHOTOS_DIR, name)));
  return albums
};

/** 添加相册 */
const createAlbum = async () => {
  const alert = new Alert();
  alert.title = i18n(['New Album', '新建相册']);
  alert.addTextField(i18n(['Input the album name', '输入相册名']));
  alert.addAction(i18n(['Save', '保存']));
  alert.addCancelAction(i18n(['Cancel', '取消']));
  const index = await alert.presentAlert();
  if (index === 0) {
    const name = alert.textFieldValue(0);
    localFile.createDirectory(
      localFile.joinPath(PHOTOS_DIR, name),
      true
    );
    return { name }
  }
};

/**
 * @param {string} album
 */
const getPhotos = (album) => {
  const dir = localFile.joinPath(PHOTOS_DIR, album);
  return localFile.listContents(dir)
    .map((filename) => {
      const albumDir = localFile.joinPath(PHOTOS_DIR, album);
      return localFile.joinPath(albumDir, filename)
    })
};

const createWidget = async () => {
  let [album] = (args.widgetParameter || '').split(',').map(str => str.trim());
  const widget = new ListWidget();
  if (!album) {
    const albums = getAlbums();
    if (albums.length > 0) {
      album = albums[0];
    } else {
      widget.addText(i18n(['Go to APP set photos', '请先去 APP 选择照片']));
      return widget
    }
  }
  const photos = getPhotos(album);
  const length = photos.length;
  if (length > 0) {
    if (await getConfig(Script.name())) {
      widget.backgroundImage = await transparent(Script.name());
    }
    widget.setPadding(0, 0, 0, 0);
    const index = Math.floor(Math.random() * length);
    const image = localFile.readImage(photos[index]);
    const imageStack = widget.addStack();
    imageStack.layoutVertically();
    imageStack.addStack().addSpacer();
    imageStack.addSpacer();
    imageStack.backgroundImage = image;
  } else {
    widget.addText(i18n([`Album "${album}" is empty`, `相册"${album}"是空的`]));
  }
  return widget
};

/** 展示相册列表 */
const presentAlbums = () => {
  const albums = localFile.listContents(PHOTOS_DIR)
    .filter((name) => localFile.isDirectory(localFile.joinPath(PHOTOS_DIR, name)));
  const table = new UITable();
  const head = new UITableRow();
  table.addRow(head);
  head.isHeader = true;
  head.addText(i18n(['Albums', '相册']));
  // 添加相册
  const cellNew = head.addButton(i18n(['New Album', '新建相册']));
  cellNew.rightAligned();
  cellNew.onTap = async () => {
    const alert = new Alert();
    alert.title = i18n(['New Album', '新建相册']);
    alert.addTextField(i18n(['Input the album name', '输入相册名']));
    alert.addAction(i18n(['Save', '保存']));
    alert.addCancelAction(i18n(['Cancel', '取消']));
    const index = await alert.presentAlert();
    if (index === 0) {
      const name = alert.textFieldValue(0);
      localFile.createDirectory(
        localFile.joinPath(PHOTOS_DIR, name),
        true
      );
      addRow(name);
      table.reload();
    }
  };
  const addRow = (album) => {
    const row = new UITableRow();
    table.addRow(row);
    const count = localFile.listContents(
      localFile.joinPath(PHOTOS_DIR, album)
    ).length;
    const cellName = row.addText(album, `${count} photos`);
    cellName.subtitleColor = new Color('#888888');
    const cellView = row.addButton(i18n(['View', '查看']));
    cellView.onTap = () => presentPhotos(album);
    const cellDelete = row.addButton(i18n(['Delete', '删除']));
    cellDelete.onTap = async () => {
      const alert = new Alert();
      alert.message = i18n([`Are you sure delete "${album}"?`, `确定删除"${album}"吗?`]);
      alert.addAction(i18n(['Delete', '删除']));
      alert.addCancelAction(i18n(['Cancel', '取消']));
      const value = await alert.presentAlert();
      if (value === 0) {
        localFile.remove(localFile.joinPath(PHOTOS_DIR, album));
        table.removeRow(row);
        table.reload();
      }
    };
  };
  for (const [, album] of albums.entries()) {
    addRow(album);
  }
  table.present();
};

/**
 * 展示相册照片
 * @param {string}
 */
const presentPhotos = (album) => {
  const photos = getPhotos(album);
  const table = new UITable();
  const head = new UITableRow();
  table.addRow(head);
  head.isHeader = true;
  head.addText(i18n(['Photos', '照片']));
  const cellChoose = head.addButton(i18n(['Choose photos', '选择图片']));
  cellChoose.rightAligned();
  cellChoose.onTap = async () => {
    const filePath = await _choosePhoto(album);
    addRow(filePath);
    table.reload();
  };

  const addRow = (filePath) => {
    const row = new UITableRow();
    table.addRow(row);
    const image = Image.fromFile(filePath);
    const cellImage = row.addImage(image);
    cellImage.widthWeight = 4;
    const dfm = new DateFormatter();
    dfm.dateFormat = 'yy-MM-dd HH:mm:ss';
    const cellName = row.addText(
      localFile.fileName(filePath, true),
      dfm.string(localFile.modificationDate(filePath))
    );
    cellName.widthWeight = 10;
    cellName.titleFont = Font.systemFont(14);
    cellName.subtitleFont = Font.lightSystemFont(10);
    const buttonPreview = row.addButton(i18n(['Preview', '查看大图']));
    buttonPreview.widthWeight = 6;
    buttonPreview.rightAligned();
    buttonPreview.onTap = () => {
      QuickLook.present(image, true);
    };
    const buttonDelete = row.addButton(i18n(['Delete', '删除']));
    buttonDelete.widthWeight = 4;
    buttonDelete.rightAligned();
    buttonDelete.onTap = () => {
      localFile.remove(filePath);
      table.removeRow(row);
      table.reload();
    };
  };

  for (const filePath of photos) {
    addRow(filePath);
  }
  QuickLook.present(table);
};

const alert = (message, title = '') => {
  const alertIns = new Alert();
  alertIns.title = title;
  alertIns.message = String(message);
  return alertIns.present()
};

await main();