/**
 * The entry point for AMP Runtime (v0.js) when AMP Runtime = AMP Doc.
 */

// src/polyfills.js must be the first import.
import './polyfills';

import {TickLabel_Enum} from '#core/constants/enums';
import {whenDocumentComplete} from '#core/document/ready';
import * as mode from '#core/mode';

import {Services} from '#service';
import {installDocService} from '#service/ampdoc-impl';
import {
  installAmpdocServices,
  installBuiltinElements,
  installRuntimeServices,
} from '#service/core-services';
import {stubElementsForDoc} from '#service/custom-element-registry';
import {installPerformanceService} from '#service/performance-impl';
import {installPlatformService} from '#service/platform-impl';

import {installAutoLightboxExtension} from './auto-lightbox';
import {startupChunk} from './chunk';
import {markUnresolvedElements} from './custom-element';
import {installErrorReporting} from './error-reporting';
import {fontStylesheetTimeout} from './font-stylesheet-timeout';
import {maybeTrackImpression} from './impression';
import {getMode} from './mode';
import {preconnectToOrigin} from './preconnect';
import {installPullToRefreshBlocker} from './pull-to-refresh';
import {adoptWithMultidocDeps} from './runtime';
import {installStandaloneExtension} from './standalone';
import {
  installStylesForDoc,
  makeBodyVisible,
  makeBodyVisibleRecovery,
} from './style-installer';
import {maybeValidate} from './validator-integration';

import {cssText as ampDocCss} from '../build/ampdoc.css';
import {cssText as ampSharedCss} from '../build/ampshared.css';

/**
 * Execute the bootstrap
 * @param {!./service/ampdoc-impl.AmpDoc} ampdoc
 * @param {!./service/performance-impl.Performance} perf
 */
function bootstrap(ampdoc, perf) {
  startupChunk(self.document, function services() {
    // Core services.
    installRuntimeServices(self);
    installAmpdocServices(ampdoc);
    // We need the core services (viewer/resources) to start instrumenting
    perf.coreServicesAvailable();
    maybeTrackImpression(self);
  });
  startupChunk(self.document, function adoptWindow() {
    adoptWithMultidocDeps(self);
  });
  startupChunk(self.document, function builtins() {
    // Builtins.
    installBuiltinElements(self);
  });
  startupChunk(self.document, function stub() {
    // Pre-stub already known elements.
    stubElementsForDoc(ampdoc);
    whenDocumentComplete(self.document).then(() => markUnresolvedElements());
  });
  startupChunk(
    self.document,
    function final() {
      if (!IS_SXG) {
        installPullToRefreshBlocker(self);
      }
      installAutoLightboxExtension(ampdoc);
      installStandaloneExtension(ampdoc);
      maybeValidate(self);
      makeBodyVisible(self.document);
      preconnectToOrigin(self.document);
    },
    /* makes the body visible */ true
  );
  startupChunk(self.document, function finalTick() {
    perf.tick(TickLabel_Enum.END_INSTALL_STYLES);
    Services.resourcesForDoc(ampdoc).ampInitComplete();
    // TODO(erwinm): move invocation of the `flush` method when we have the
    // new ticks in place to batch the ticks properly.
    perf.flush();
  });
}

// Store the originalHash as early as possible. Trying to debug:
// https://github.com/ampproject/amphtml/issues/6070
if (self.location) {
  self.location['originalHash'] = self.location.hash;
}

/** @type {!./service/ampdoc-impl.AmpDocService} */
let ampdocService;
// We must under all circumstances call makeBodyVisible.
// It is much better to have AMP tags not rendered than having
// a completely blank page.
try {
  // Should happen first.
  installErrorReporting(self); // Also calls makeBodyVisibleRecovery on errors.

  // Declare that this runtime will support a single root doc. Should happen
  // as early as possible.
  installDocService(self, /* isSingleDoc */ true);
  ampdocService = Services.ampdocServiceFor(self);
} catch (e) {
  // In case of an error call this.
  makeBodyVisibleRecovery(self.document);
  throw e;
}
startupChunk(self.document, function initial() {
  /** @const {!./service/ampdoc-impl.AmpDoc} */
  const ampdoc = ampdocService.getAmpDoc(self.document);
  installPlatformService(self);
  installPerformanceService(self);
  /** @const {!./service/performance-impl.Performance} */
  const perf = Services.performanceFor(self);
  if (mode.isEsm()) {
    perf.addEnabledExperiment('esm');
  }
  fontStylesheetTimeout(self);
  perf.tick(TickLabel_Enum.INSTALL_STYLES);
  if (mode.isEsm()) {
    bootstrap(ampdoc, perf);
    return;
  }

  installStylesForDoc(
    ampdoc,
    ampDocCss + ampSharedCss,
    () => bootstrap(ampdoc, perf),
    /* opt_isRuntimeCss */ true,
    /* opt_ext */ 'amp-runtime'
  );
});

// Output a message to the console and add an attribute to the <html>
// tag to give some information that can be used in error reports.
// (At least by sophisticated users).
if (self.console) {
  (console.info || console.log).call(
    console,
    `Powered by AMP ⚡ HTML – Version ${mode.version()}`,
    self.location.href
  );
}
// This code is eliminated in prod build through a babel transformer.
if (getMode().localDev) {
  self.document.documentElement.setAttribute('esm', mode.isEsm() ? 1 : 0);
}
self.document.documentElement.setAttribute('amp-version', mode.version());
