const Koa = require('koa');
const Session = require('@cardstack/plugin-utils/session');
const { Registry, Container } = require('@cardstack/di');
const postgresConfig = require('@cardstack/plugin-utils/postgres-config');
// lazy load only in container mode, since they uses node 8 features
let EmberConnection;
let Orchestrator;

const log = require('@cardstack/logger')('cardstack/server');


async function wireItUp(projectDir, encryptionKeys, dataSources, opts = {}) {
  let registry = new Registry();
  registry.register('config:project', {
    path: projectDir
  });
  registry.register('config:data-sources', dataSources);
  registry.register('config:encryption-key', encryptionKeys);
  registry.register('config:public-url', { url: opts.url });
  registry.register('config:ci-session', { id: opts.ciSessionId });
  registry.register('config:pg-boss', postgresConfig(Object.assign({
    database: process.env.PG_BOSS_DATABASE || `pgboss_${opts.environment}`,
    host:     process.env.PG_BOSS_HOST,
    user:     process.env.PG_BOSS_USER,
    port:     process.env.PG_BOSS_PORT,
    password: process.env.PG_BOSS_PASSWORD
  }, opts.pgBossConfig)));

  if (typeof opts.seeds === 'function') {
    registry.register('config:initial-models', opts.seeds);
  } else {
    registry.register('config:initial-models', () => []);
  }

  let container = new Container(registry);

  // in the test suite we want more deterministic control of when
  // indexing happens
  if (!opts.disableAutomaticIndexing) {
    await container.lookup(`plugin-client:${require.resolve('@cardstack/pgsearch/client')}`).ensureDatabaseSetup();

    // some datasources are dependent upon a sync at boot for index of pristine system
    container.lookup('hub:indexers').update(); // dont await boot-time indexing, invalidation logic has our back
    setInterval(() => container.lookup('hub:indexers').update(), 600000);
  }

  // this registration pattern is how we make broccoli wait for our
  // asynchronous startup stuff before running the first build.
  if (opts.broccoliConnector) {
    opts.broccoliConnector.setSource(container.lookup('hub:code-generators'));
  }

  return container;
}

async function loadSeeds(container, seedModels, opts) {
  if (!container) { return; }

  let branch = opts && opts.branch || 'master';
  let writers = container.lookup('hub:writers');

  for (let model of seedModels) {
    await writers.create(branch, Session.INTERNAL_PRIVILEGED, model.type, { data: model });
  }

  await container.lookup('hub:indexers').update({ forceRefresh: true });
}

async function makeServer(projectDir, encryptionKeys, dataSources, opts = {}) {
  let readyResolver;
  if (opts.containerized) {
    log.debug('Running in container mode');
    // lazy loading
    Orchestrator = Orchestrator || require('./docker-container/orchestrator');
    EmberConnection = EmberConnection || require('./docker-container/ember-connection');

    let orchestrator = new Orchestrator(opts.leaveServicesRunning);
    orchestrator.start();

    let readyPromise = new Promise(function(r) { readyResolver = r; });

    // Eventually we'll pass a connection instance into the hub, for triggering rebuilds.
    // For now, it just has the side effect of shutting the hub down properly when it's time.
    new EmberConnection({
      orchestrator,
      ready: readyPromise,
      heartbeat: opts.heartbeat
    });

    await orchestrator.ready;
  }

  log.info('Starting main hub server');
  let container = await wireItUp(projectDir, encryptionKeys, dataSources, opts);
  let app = new Koa();
  app.use(httpLogging);
  app.use(container.lookup('hub:middleware-stack').middleware());
  log.info('Main hub initialized');
  if (opts.containerized) { readyResolver(); }
  return app;
}

async function httpLogging(ctxt, next) {
  log.info('start %s %s', ctxt.request.method, ctxt.request.originalUrl);
  await next();
  log.info('finish %s %s %s', ctxt.request.method, ctxt.request.originalUrl, ctxt.response.status);
}


exports.wireItUp = wireItUp;
exports.makeServer = makeServer;
exports.loadSeeds = loadSeeds;

@@ -1617,142 +1617,56 @@
 ();%0A
-%0A
     
-// some datasources are dependent upon a sync at boot for index of pristine system%0A    container.lookup('hub:indexers').update(
+startIndexing(opts.environment, container
 );
+ 
  // 
@@ -1732,82 +1732,8 @@
 ack%0A
-    setInterval(() =%3E container.lookup('hub:indexers').update(), 600000);%0A
   %7D%0A
@@ -2000,24 +2000,1031 @@
 ntainer;%0A%7D%0A%0A
+async function startIndexing(environment, container) %7B%0A  // some datasources are dependent upon a sync at boot for index of pristine system%0A  await container.lookup('hub:indexers').update();%0A%0A  if (environment !== 'production') %7B%0A    let ephemeralStorage = await container.lookup(%60plugin-services:$%7Brequire.resolve('@cardstack/ephemeral/service')%7D%60);%0A    let searchers = await container.lookup(%60hub:searchers%60);%0A    let controllingBranch = await container.lookup(%60hub:controlling-branch%60);%0A    let models = await (await container.lookup('config:initial-models'))();%0A%0A    await ephemeralStorage.validateModels(models, async (type, id) =%3E %7B%0A      let result;%0A      try %7B%0A        result = await searchers.get(Session.INTERNAL_PRIVILEGED, controllingBranch.name, type, id);%0A      %7D catch (err) %7B%0A        if (err.status !== 404) %7B throw err; %7D%0A      %7D%0A%0A      if (result && result.data) %7B%0A        return result.data;%0A      %7D%0A    %7D);%0A  %7D%0A%0A  setInterval(() =%3E container.lookup('hub:indexers').update(), 600000);%0A%7D%0A%0A
 async functi

validate ephemeral models when hub is launched in non-production environment