"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = dumpGraphToGraphViz;
var _BundleGraph = require("./BundleGraph");
var _RequestTracker = require("./RequestTracker");
function _path() {
  const data = _interopRequireDefault(require("path"));
  _path = function () {
    return data;
  };
  return data;
}
function _graph() {
  const data = require("@parcel/graph");
  _graph = function () {
    return data;
  };
  return data;
}
var _projectPath = require("./projectPath");
var _types = require("./types");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const COLORS = {
  root: 'gray',
  asset: 'green',
  dependency: 'orange',
  transformer_request: 'cyan',
  file: 'gray',
  default: 'white'
};
const TYPE_COLORS = {
  // bundle graph
  bundle: 'blue',
  contains: 'grey',
  internal_async: 'orange',
  references: 'red',
  sibling: 'green',
  // asset graph
  // request graph
  invalidated_by_create: 'green',
  invalidated_by_create_above: 'orange',
  invalidate_by_update: 'cyan',
  invalidated_by_delete: 'red'
};
async function dumpGraphToGraphViz(graph, name, edgeTypes) {
  var _globalThis$PARCEL_DU;
  if ("production" === 'production' && !process.env.PARCEL_BUILD_REPL) {
    return;
  }
  let mode = process.env.PARCEL_BUILD_REPL ? // $FlowFixMe
  (_globalThis$PARCEL_DU = globalThis.PARCEL_DUMP_GRAPHVIZ) === null || _globalThis$PARCEL_DU === void 0 ? void 0 : _globalThis$PARCEL_DU.mode : process.env.PARCEL_DUMP_GRAPHVIZ;

  // $FlowFixMe[invalid-compare]
  if (mode == null || mode == false) {
    return;
  }
  let detailedSymbols = mode === 'symbols';
  let GraphVizGraph = require('graphviz/lib/deps/graph').Graph;
  let g = new GraphVizGraph(null, 'G');
  g.type = 'digraph';
  // $FlowFixMe
  for (let [id, node] of graph.nodes.entries()) {
    if (node == null) continue;
    let n = g.addNode(nodeId(id));
    // $FlowFixMe default is fine. Not every type needs to be in the map.
    n.set('color', COLORS[node.type || 'default']);
    n.set('shape', 'box');
    n.set('style', 'filled');
    let label;
    if (typeof node === 'string') {
      label = node;
    } else if (node.assets) {
      label = `(${nodeId(id)}), (assetIds: ${[...node.assets].map(a => {
        let arr = a.filePath.split('/');
        return arr[arr.length - 1];
      }).join(', ')}) (sourceBundles: ${[...node.sourceBundles].join(', ')}) (bb ${node.bundleBehavior ?? 'none'})`;
    } else if (node.type) {
      label = `[${(0, _graph().fromNodeId)(id)}] ${node.type || 'No Type'}: [${node.id}]: `;
      if (node.type === 'dependency') {
        label += node.value.specifier;
        let parts = [];
        if (node.value.priority !== _types.Priority.sync) {
          var _Object$entries$find;
          parts.push((_Object$entries$find = Object.entries(_types.Priority).find(([, v]) => v === node.value.priority)) === null || _Object$entries$find === void 0 ? void 0 : _Object$entries$find[0]);
        }
        if (node.value.isOptional) parts.push('optional');
        if (node.value.specifierType === _types.SpecifierType.url) parts.push('url');
        if (node.hasDeferred) parts.push('deferred');
        if (node.deferred) parts.push('deferred');
        if (node.excluded) parts.push('excluded');
        if (parts.length) label += ' (' + parts.join(', ') + ')';
        if (node.value.env) label += ` (${getEnvDescription(node.value.env)})`;
        let depSymbols = node.value.symbols;
        if (detailedSymbols) {
          if (depSymbols) {
            if (depSymbols.size) {
              label += '\\nsymbols: ' + [...depSymbols].map(([e, {
                local
              }]) => [e, local]).join(';');
            }
            let weakSymbols = [...depSymbols].filter(([, {
              isWeak
            }]) => isWeak).map(([s]) => s);
            if (weakSymbols.length) {
              label += '\\nweakSymbols: ' + weakSymbols.join(',');
            }
            if (node.usedSymbolsUp.size > 0) {
              label += '\\nusedSymbolsUp: ' + [...node.usedSymbolsUp].map(([s, sAsset]) => sAsset ? `${s}(${sAsset.asset}.${sAsset.symbol ?? ''})` : sAsset === null ? `${s}(external)` : `${s}(ambiguous)`).join(',');
            }
            if (node.usedSymbolsDown.size > 0) {
              label += '\\nusedSymbolsDown: ' + [...node.usedSymbolsDown].join(',');
            }
            // if (node.usedSymbolsDownDirty) label += '\\nusedSymbolsDownDirty';
            // if (node.usedSymbolsUpDirtyDown)
            //   label += '\\nusedSymbolsUpDirtyDown';
            // if (node.usedSymbolsUpDirtyUp) label += '\\nusedSymbolsUpDirtyUp';
          } else {
            label += '\\nsymbols: cleared';
          }
        }
      } else if (node.type === 'asset') {
        label += _path().default.basename((0, _projectPath.fromProjectPathRelative)(node.value.filePath)) + '#' + node.value.type;
        if (detailedSymbols) {
          if (!node.value.symbols) {
            label += '\\nsymbols: cleared';
          } else if (node.value.symbols.size) {
            label += '\\nsymbols: ' + [...node.value.symbols].map(([e, {
              local
            }]) => [e, local]).join(';');
          }
          if (node.usedSymbols.size) {
            label += '\\nusedSymbols: ' + [...node.usedSymbols].join(',');
          }
          // if (node.usedSymbolsDownDirty) label += '\\nusedSymbolsDownDirty';
          // if (node.usedSymbolsUpDirty) label += '\\nusedSymbolsUpDirty';
        } else {
          label += '\\nsymbols: cleared';
        }
      } else if (node.type === 'asset_group') {
        if (node.deferred) label += '(deferred)';
      } else if (node.type === 'file') {
        label += _path().default.basename(node.id);
      } else if (node.type === 'transformer_request') {
        label += _path().default.basename(node.value.filePath) + ` (${getEnvDescription(node.value.env)})`;
      } else if (node.type === 'bundle') {
        let parts = [];
        if (node.value.needsStableName) parts.push('stable name');
        parts.push(node.value.name);
        parts.push('bb:' + (node.value.bundleBehavior ?? 'null'));
        if (node.value.isPlaceholder) parts.push('placeholder');
        if (parts.length) label += ' (' + parts.join(', ') + ')';
        if (node.value.env) label += ` (${getEnvDescription(node.value.env)})`;
      } else if (node.type === 'request') {
        label = node.requestType + ':' + node.id;
      }
    }
    n.set('label', label);
  }
  let edgeNames;
  if (edgeTypes) {
    edgeNames = Object.fromEntries(Object.entries(edgeTypes).map(([k, v]) => [v, k]));
  }
  for (let edge of graph.getAllEdges()) {
    let gEdge = g.addEdge(nodeId(edge.from), nodeId(edge.to));
    let color = null;
    if (edge.type != 1 && edgeNames) {
      color = TYPE_COLORS[edgeNames[edge.type]];
    }
    if (color != null) {
      gEdge.set('color', color);
    }
  }
  if (process.env.PARCEL_BUILD_REPL) {
    var _globalThis$PARCEL_DU2;
    // $FlowFixMe
    (_globalThis$PARCEL_DU2 = globalThis.PARCEL_DUMP_GRAPHVIZ) === null || _globalThis$PARCEL_DU2 === void 0 || _globalThis$PARCEL_DU2.call(globalThis, name, g.to_dot());
  } else {
    const tempy = require('tempy');
    let tmp = tempy.file({
      name: `parcel-${name}.png`
    });
    await g.output('png', tmp);
    // eslint-disable-next-line no-console
    console.log('Dumped', tmp);
  }
}
function nodeId(id) {
  // $FlowFixMe
  return `node${id}`;
}
function getEnvDescription(env) {
  let description;
  if (typeof env.engines.browsers === 'string') {
    description = `${env.context}: ${env.engines.browsers}`;
  } else if (Array.isArray(env.engines.browsers)) {
    description = `${env.context}: ${env.engines.browsers.join(', ')}`;
  } else if (env.engines.node) {
    description = `node: ${env.engines.node}`;
  } else if (env.engines.electron) {
    description = `electron: ${env.engines.electron}`;
  }
  return description ?? '';
}