import { configuration } from "./configuration.js";
import { objectKeepKeys } from "./js-utils.js";
import { DevError } from "./errors.js";

export function checkEquivalence(a, b) {
  if (!a || !b || !a.type || !b.type || a.type !== b.type) return false;
  switch (a.type) {
    case "legacy_block":
      return a.name === b.name
          && a.metadata === b.metadata
          && (a.blockstate === undefined && b.blockstate === undefined
              || JSON.stringify(a.blockstate) === JSON.stringify(b.blockstate));

    case "legacy_fluid":
      return a.name === b.name;

    case "legacy_item":
      return a.name === b.name
          && a.metadata === b.metadata;

    case "oredict":
      return a.oredict === b.oredict;

    case "list":
      return a.elements.length === b.elements.length &&
          a.elements.every((aE, i) => checkEquivalence(aE, b.elements[i]));

    default:
      console.warn("unknown type", a.type, a, b);
  }
  return false;
}

export function stripEntryToMinimums(entry) {
  if (!entry.type) throw new DevError("must have a type");
  switch (entry.type) {
    case "legacy_block":
      return objectKeepKeys(entry, "type", "name", "metadata", "blockstate");

    case "legacy_fluid":
      return objectKeepKeys(entry, "type", "name");

    case "oredict":
      return objectKeepKeys(entry, "type", "oredict");

    case "list":
      return {
        type: entry.type,
        elements: entry.elements.map(stripEntryToMinimums),
      };
  }
  throw new DevError(`unknown entry type ${entry.type}`);
}

export function findFullEntry(type, entry, { configurationHint, all = false } = {}) {
  const possibleEntries = [
    ...Object.entries(configuration.configuration),
    ...configuration.addons.flatMap(a => Object.entries(a.configuration)),
  ]
      .filter(([k, v]) => (!configurationHint || k === configurationHint) && type in v)
      .flatMap(([, v]) => v[type]);
  if (all) {
    return possibleEntries.filter(other => checkEquivalence(entry, other));
  }
  return possibleEntries.find(other => checkEquivalence(entry, other));
}

export function findFullEntryFromAtlasMetadata(metadata, options = {}) {
  return findFullEntry(
      metadata.configurationList,
      metadata,
      {
        configurationHint: metadata.configuration,
        ...options,
      },
  );
}

export function findFullEntryAnywhere(entry, { configurationHint } = {}) {
  const possibleEntries = [
    ...Object.entries(configuration.configuration),
    ...configuration.addons.flatMap(a => Object.entries(a.configuration)),
  ]
      .filter(([k]) => (!configurationHint || k === configurationHint))
      .flatMap(([, v]) => Object.entries(v)
          .flatMap(([k, v]) => k === "modules" ? [
              ...("plannerator:global_elements" in v
                  ? [["plannerator:global_elements", v["plannerator:global_elements"].elements]]
                  : []),
          ] : [[k, v]]))
      .flatMap(([t, v]) => v.map(e => [t, e]));
  const findResult = possibleEntries.find(([, other]) => checkEquivalence(entry, other));
  if (!findResult) {
    console.log("did not find", entry, "in", possibleEntries);
    return null;
  }
  const [ type, foundEntry ] = findResult;
  return { type, entry: foundEntry };
}

export function resolveBlockName(block, { configurationHint }) {

  if (block.type === "module") {
    switch (block.name) {
      case "nuclearcraft:overhaul_sfr:fuel_cell":
        return "Fuel Cell";

      case "nuclearcraft:overhaul_sfr:moderator":
        return "Moderator";

      case "nuclearcraft:overhaul_sfr:casing":
        return "Casing";

      case "nuclearcraft:overhaul_sfr:reflector":
        return "Reflector";

      case "nuclearcraft:overhaul_sfr:neutron_shield":
        return "Neutron Shield";

      case "nuclearcraft:overhaul_sfr:irradiator":
        return "Irradiator";

      default:
        console.warn("unknown module", block);
        return null;
    }
  }

  const possibleBlocks = [
    ...Object.entries(configuration.configuration),
    ...configuration.addons.flatMap(a => Object.entries(a.configuration)),
  ]
      .filter(([k, v]) => (!configurationHint || k === configurationHint) && "blocks" in v)
      .flatMap(([, v]) => v.blocks);

  const entry = possibleBlocks.find(other => checkEquivalence(block, other));
  if (!entry) return null;

  return entry.modules["plannerator:display_name"].display_name;
}

export function lookupFromMetadata(metadata) {
  if (!metadata.configuration || !metadata.configurationList || !metadata.type)
    return null;

  const possibleEntries = [
    ...Object.entries(configuration.configuration),
    ...configuration.addons.flatMap(a => Object.entries(a.configuration)),
  ]
      .filter(([k, v]) => k === metadata.configuration && metadata.configurationList in v)
      .flatMap(([, v]) => v[metadata.configurationList])
      .filter(v => v.type === metadata.type);

  return possibleEntries.find(other => checkEquivalence(metadata, other));
}
