/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

import path from 'node:path';
import { fileURLToPath } from 'node:url';
import fs from 'node:fs';
import { styleText } from 'node:util';

import { fdir } from 'fdir';

import dataFolders from '../../scripts/lib/data-folders.js';
import walk from '../../utils/walk.js';
import stringifyAndOrderProperties from '../lib/stringify-and-order-properties.js';

const dirname = fileURLToPath(new URL('.', import.meta.url));

/**
 * Updates the specified key in the given JSON object using the provided updater function.
 * @param {string} key The key to update in dot notation (e.g., 'api.foobar').
 * @param {*} json The JSON object to update.
 * @param {(arg: *) => *} updater The function to apply to the '__compat' property of the value corresponding to the key.
 * @returns {*} The updated JSON object.
 */
const performUpdate = (key, json, updater) => {
  const parts = key.split('.');
  if (!(parts[0] in json)) {
    console.warn('Key not found in file!');
    return json;
  }
  if (parts.length === 1) {
    json[parts[0]]['__compat'] = updater(json[parts[0]]['__compat']);
  } else {
    json[parts[0]] = performUpdate(
      parts.slice(1).join('.'),
      json[parts[0]],
      updater,
    );
  }
  return json;
};

/**
 * Updates features in multiple JSON files based on the provided feature IDs and updater function.
 * @param {string[]} featureIDs An array of feature IDs to update.
 * @param {(arg: *) => *} updater The updater function to apply to each matching feature.
 * @returns {void}
 */
export const updateFeatures = (featureIDs, updater) => {
  for (const dir of dataFolders) {
    const paths = /** @type {string[]} */ (
      new fdir()
        .withBasePath()
        .filter((fp) => fp.endsWith('.json'))
        .crawl(path.join(dirname, '..', '..', dir))
        .sync()
    );

    for (const fp of paths) {
      const rawcontents = fs.readFileSync(fp);
      const contents = JSON.parse(rawcontents.toString('utf8'));
      let changed = false;

      const applyToAnyFeatureID = !featureIDs || featureIDs.length === 0;
      const walker = walk(undefined, contents);
      for (const { path: featureID } of walker) {
        if (
          applyToAnyFeatureID ||
          featureIDs.some(
            (fid) =>
              fid === featureID ||
              (fid.endsWith('*') && featureID.startsWith(fid.slice(0, -1))),
          )
        ) {
          const before = JSON.stringify(contents, undefined, 2);
          const after = JSON.stringify(
            performUpdate(featureID, contents, updater),
            undefined,
            2,
          );
          if (before != after) {
            console.log(styleText('yellow', `Updated ${featureID}`));
            changed = true;
          }
        }
      }

      if (changed) {
        fs.writeFileSync(
          fp,
          stringifyAndOrderProperties(contents) + '\n',
          'utf-8',
        );
      }
    }
  }
};
