/**
 * Debian-specific rollup config for node-lit.
 *
 * Uses only plugins available as Debian packages.  Replaces the upstream
 * rollup-common.js during the package build.
 *
 * Differences from upstream:
 *  - rollup-plugin-summary omitted (build stats only, no effect on output)
 *  - rollup-plugin-copy omitted (only copies HTML test files; we skip tests)
 *  - @rollup/plugin-virtual / name-cache seeder omitted (needs packaging);
 *    the stable cross-package properties are still pre-seeded directly into
 *    the terser name cache, which is what matters for correctness.
 */

import {terser} from 'rollup-plugin-terser';
import replace from '@rollup/plugin-replace';
import sourcemaps from 'rollup-plugin-sourcemaps';
import nodeResolve from '@rollup/plugin-node-resolve';
import inject from '@rollup/plugin-inject';

// ---------------------------------------------------------------------------
// Terser name-cache setup (adapted from upstream rollup-common.js)
//
// Properties starting with _$ are accessed across package boundaries and must
// mangle to the same letter in every package.  We pre-seed the name cache
// with the upstream-defined stable mapping so terser never re-uses those
// letters for other properties.
//
// ONCE A MANGLED NAME HAS BEEN ASSIGNED HERE IT MUST NEVER CHANGE.
// ---------------------------------------------------------------------------

const STABLE_PROPERTY_PREFIX = '_$A';

const PACKAGE_CLASS_PREFIXES = {
  lit: '_$B',
  'lit-html': '_$C',
  'lit-element': '_$D',
  '@lit/reactive-element': '_$E',
  '@lit/context': '_$Q',
};

// Properties reserved as-is (never mangled).
const reservedProperties = [
  '_$litType$',
  '_$litDirective$',
  '_$litPart$',
  '_$litElement$',
  '_$litStatic$',
  '_$cssResult$',
  '_$litProps$',
];

// Stable cross-package properties with their fixed mangled names.
const stableProperties = {
  _$startNode: 'A',
  _$endNode: 'B',
  _$getTemplate: 'C',
  _$template: 'D',
  _$didUpdate: 'E',
  _$renderImpl: 'F',
  _$needsHydration: 'G',
  _$committedValue: 'H',
  _$setValue: 'I',
  _$handlesPrepareStyles: 'J',
  _$attributeToProperty: 'K',
  _$changedProperties: 'L',
  _$parent: 'M',
  _$disconnectableChildren: 'N',
  _$notifyDirectiveConnectionChanged: 'O',
  _$notifyConnectionChanged: 'P',
  _$reparentDisconnectables: 'Q',
  _$clear: 'R',
  _$resolve: 'S',
  _$initialize: 'T',
  _$isConnected: 'U',
  _$parts: 'V',
  _$litServerRenderMode: 'W',
};

const makeNameCache = () => ({
  props: {
    props: Object.fromEntries(
      Object.entries(stableProperties).map(([name, val]) => [
        '$' + name,
        STABLE_PROPERTY_PREFIX + val,
      ])
    ),
  },
});

const addedClassPrefix = new WeakSet();
const reactiveElementInclude = [
  '**/packages/reactive-element/development/reactive-element.js',
];
const productionReplacements = {
  'const DEV_MODE = true': 'const DEV_MODE = false',
  'const ENABLE_EXTRA_SECURITY_HOOKS = true':
    'const ENABLE_EXTRA_SECURITY_HOOKS = false',
  'const ENABLE_SHADYDOM_NOPATCH = true':
    'const ENABLE_SHADYDOM_NOPATCH = false',
};
const nodeInject = () =>
  inject({
    HTMLElement: ['@lit-labs/ssr-dom-shim', 'HTMLElement'],
    customElements: ['@lit-labs/ssr-dom-shim', 'customElements'],
    include: reactiveElementInclude,
  });
const nodeExtendsPatch = () =>
  replace({
    preventAssignment: true,
    values: {
      'extends HTMLElement': 'extends (globalThis.HTMLElement ?? HTMLElement)',
    },
    include: reactiveElementInclude,
  });

const generateTerserOptions = (nameCache, classPropertyPrefix = '') => ({
  ecma: 2021,
  compress: {unsafe: true, passes: 2},
  output: {comments: 'some', inline_script: false},
  get nameCache() {
    if (nameCache && !addedClassPrefix.has(this)) {
      const props = nameCache.props.props;
      for (const p in props) {
        if (p.startsWith('$__') && !props[p].startsWith(classPropertyPrefix)) {
          props[p] = classPropertyPrefix + props[p];
        }
      }
      addedClassPrefix.add(this);
    }
    return nameCache;
  },
  mangle: {
    properties: {
      regex: /^_/,
      reserved: reservedProperties,
      debug: false,
    },
  },
});

// ---------------------------------------------------------------------------
// litProdConfig — mirrors the upstream API used by each package's
// rollup.config.js
// ---------------------------------------------------------------------------

export function litProdConfig({
  entryPoints,
  external = [],
  bundled = [],
  packageName,
  outputDir = './',
  includeNodeBuild = false,
} = {}) {
  const classPropertyPrefix = PACKAGE_CLASS_PREFIXES[packageName] ?? '';
  const nameCache = makeNameCache();
  const terserOptions = generateTerserOptions(nameCache, classPropertyPrefix);
  const prodReplace = {
    preventAssignment: true,
    values: productionReplacements,
  };
  const nodeBuild = ({dir, minify}) => ({
    input: entryPoints.map((name) => `development/${name}.js`),
    output: {
      dir: `${outputDir}/node${dir}`,
      format: 'esm',
      preserveModules: true,
      sourcemap: true,
    },
    external: [...external, '@lit-labs/ssr-dom-shim'],
    plugins: [
      replace({
        preventAssignment: true,
        values: {
          'const NODE_MODE = false': 'const NODE_MODE = true',
          ...(minify ? productionReplacements : {}),
          'const ENABLE_SHADYDOM_NOPATCH = true':
            'const ENABLE_SHADYDOM_NOPATCH = false',
        },
      }),
      nodeInject(),
      nodeExtendsPatch(),
      sourcemaps(),
      ...(minify ? [terser(terserOptions)] : []),
    ],
  });

  return [
    // Production ESM build (minified, preserves module structure)
    {
      input: entryPoints.map((name) => `development/${name}.js`),
      output: {
        dir: outputDir,
        format: 'esm',
        preserveModules: true,
        sourcemap: true,
      },
      external,
      plugins: [replace(prodReplace), sourcemaps(), terser(terserOptions)],
    },

    // Node.js build (minified, imports the packaged ssr-dom-shim at runtime)
    ...(includeNodeBuild
      ? [
          nodeBuild({dir: '', minify: true}),
          nodeBuild({dir: '/development', minify: false}),
        ]
      : []),

    // Standalone bundles (polyfill-support, lit-core.min, lit-all.min, etc.)
    ...bundled.map(({file, output, name, format = 'umd'}) => ({
      input: `development/${file}.js`,
      output: {
        file: `${output || file}.js`,
        format,
        name,
        sourcemap: true,
      },
      plugins: [
        nodeResolve({exportConditions: ['development']}),
        replace({
          preventAssignment: false,
          values: {
            ...productionReplacements,
            'var DEV_MODE = true': 'var DEV_MODE = false',
            'var ENABLE_EXTRA_SECURITY_HOOKS = true':
              'var ENABLE_EXTRA_SECURITY_HOOKS = false',
            'var ENABLE_SHADYDOM_NOPATCH = true':
              'var ENABLE_SHADYDOM_NOPATCH = false',
          },
        }),
        sourcemaps(),
        terser(terserOptions),
      ],
    })),
  ];
}
