<?php

$conn_w = id(new PhabricatorMetaMTAMail())->establishConnection('w');
$lists = new LiskRawMigrationIterator($conn_w, 'metamta_mailinglist');

echo pht('Migrating mailing lists...')."\n";

foreach ($lists as $list) {
  $name = $list['name'];
  $email = $list['email'];
  $uri = $list['uri'];
  $old_phid = $list['phid'];

  $username = preg_replace('/[^a-zA-Z0-9_-]+/', '-', $name);
  $username = preg_replace('/-{2,}/', '-', $username);
  $username = trim($username, '-');
  if (!strlen($username)) {
    $username = 'mailinglist';
  }
  $username .= '-list';

  $username_okay = false;
  for ($suffix = 1; $suffix <= 9; $suffix++) {
    if ($suffix == 1) {
      $effective_username = $username;
    } else {
      $effective_username = $username.$suffix;
    }

    $collision = id(new PhabricatorPeopleQuery())
      ->setViewer(PhabricatorUser::getOmnipotentUser())
      ->withUsernames(array($effective_username))
      ->executeOne();
    if (!$collision) {
      $username_okay = true;
      break;
    }
  }

  if (!$username_okay) {
    echo pht(
      'Failed to migrate mailing list "%s": unable to generate a unique '.
      'username for it.')."\n";
    continue;
  }

  $username = $effective_username;
  if (!PhabricatorUser::validateUsername($username)) {
    echo pht(
      'Failed to migrate mailing list "%s": unable to generate a valid '.
      'username for it.',
      $name)."\n";
    continue;
  }

  $address = id(new PhabricatorUserEmail())->loadOneWhere(
    'address = %s',
    $email);
  if ($address) {
    echo pht(
      'Failed to migrate mailing list "%s": an existing user already '.
      'has the email address "%s".',
      $name,
      $email)."\n";
    continue;
  }

  $user = id(new PhabricatorUser())
    ->setUsername($username)
    ->setRealName(pht('Mailing List "%s"', $name))
    ->setIsApproved(1)
    ->setIsMailingList(1);

  $email_object = id(new PhabricatorUserEmail())
    ->setAddress($email)
    ->setIsVerified(1);

  try {
    id(new PhabricatorUserEditor())
      ->setActor($user)
      ->createNewUser($user, $email_object);
  } catch (Exception $ex) {
    echo pht(
      'Failed to migrate mailing list "%s": %s.',
      $name,
      $ex->getMessage())."\n";
    continue;
  }

  $new_phid = $user->getPHID();

  // NOTE: After the PHID type is removed we can't use any Edge code to
  // modify edges.

  $edge_type = PhabricatorSubscribedToObjectEdgeType::EDGECONST;
  $edge_inverse = PhabricatorObjectHasSubscriberEdgeType::EDGECONST;

  $map = PhabricatorPHIDType::getAllTypes();
  foreach ($map as $type => $spec) {
    try {
      $object = $spec->newObject();
      if (!$object) {
        continue;
      }
      $object_conn_w = $object->establishConnection('w');
      queryfx(
        $object_conn_w,
        'UPDATE %T SET dst = %s WHERE dst = %s AND type = %s',
        PhabricatorEdgeConfig::TABLE_NAME_EDGE,
        $new_phid,
        $old_phid,
        $edge_inverse);
    } catch (Exception $ex) {
      // Just ignore these; they're mostly tables not existing.
      continue;
    }
  }

  try {
    $dst_phids = queryfx_all(
      $conn_w,
      'SELECT dst FROM %T WHERE src = %s AND type = %s',
      PhabricatorEdgeConfig::TABLE_NAME_EDGE,
      $old_phid,
      $edge_type);
    if ($dst_phids) {
      $editor = new PhabricatorEdgeEditor();
      foreach ($dst_phids as $dst_phid) {
        $editor->addEdge($new_phid, $edge_type, $dst_phid['dst']);
      }
      $editor->save();
    }
  } catch (Exception $ex) {
    echo pht(
      'Unable to migrate some inverse edges for mailing list "%s": %s.',
      $name,
      $ex->getMessage())."\n";
    continue;
  }

  echo pht(
    'Migrated mailing list "%s" to mailing list user "%s".',
    $name,
    $user->getUsername())."\n";
}