import { objectKeys } from "@scalar/helpers/object/object-keys";
import { getResolvedRef } from "@scalar/workspace-store/helpers/get-resolved-ref";
import { resolve } from "@scalar/workspace-store/resolve";
import { isArraySchema } from "@scalar/workspace-store/schemas/v3.1/strict/type-guards";
const mergeAllOfSchemas = (schemas, rootSchema) => {
  if (!schemas?.allOf?.length || !Array.isArray(schemas.allOf)) {
    return rootSchema || {};
  }
  const result = {};
  const { allOf: _, ...baseSchema } = schemas;
  for (const _schema of schemas.allOf) {
    if (!_schema || typeof _schema !== "object") {
      continue;
    }
    const schema = resolve.schema(_schema);
    if (schema.allOf) {
      const nestedMerged = mergeAllOfSchemas(schema);
      mergeSchemaIntoResult(result, nestedMerged);
      continue;
    }
    mergeSchemaIntoResult(result, schema);
  }
  if (Object.keys(baseSchema).length > 0) {
    mergeSchemaIntoResult(result, baseSchema, true);
  }
  if (rootSchema && typeof rootSchema === "object") {
    if (rootSchema.allOf) {
      const nestedMerged = mergeAllOfSchemas(rootSchema);
      mergeSchemaIntoResult(result, nestedMerged, true);
    } else {
      mergeSchemaIntoResult(result, rootSchema, true);
    }
  }
  return result;
};
const mergeSchemaIntoResult = (result, schema, override = false) => {
  const schemaKeys = objectKeys(schema);
  if (schemaKeys.length === 0) {
    return;
  }
  for (const key of schemaKeys) {
    const value = getResolvedRef(schema[key]);
    if (value === void 0) {
      continue;
    }
    if (key === "required") {
      if (Array.isArray(value) && value.length > 0) {
        if (result.required?.length) {
          result.required = [.../* @__PURE__ */ new Set([...result.required, ...value])];
        } else {
          result.required = value.slice();
        }
      }
    } else if (key === "properties") {
      if (value && typeof value === "object") {
        if (!result.properties) {
          result.properties = {};
        }
        mergePropertiesIntoResult(result.properties, value);
      }
    } else if (key === "items") {
      const items = resolve.schema(value);
      if (items) {
        if (isArraySchema(schema)) {
          if (!result.items) {
            result.items = {};
          }
          if (items.allOf) {
            const mergedItems = mergeAllOfSchemas(items);
            Object.assign(result.items, mergedItems);
          } else {
            mergeItemsIntoResult(getResolvedRef(result.items), items);
          }
        } else if (items.allOf) {
          const mergedItems = mergeAllOfSchemas(items);
          if ("properties" in mergedItems) {
            if (!("properties" in result)) {
              result.properties = {};
            }
            "properties" in result && mergePropertiesIntoResult(result.properties, mergedItems.properties);
          }
        } else if (!("items" in result)) {
          result.items = items;
        }
      }
    } else if (key === "enum") {
      if (Array.isArray(value) && value.length > 0) {
        result.enum = [.../* @__PURE__ */ new Set([...result.enum || [], ...value])];
      }
    } else if (key === "oneOf" || key === "anyOf") {
      if (Array.isArray(value)) {
        if (!("properties" in result)) {
          result.properties = {};
        }
        for (const _option of value) {
          const option = resolve.schema(_option);
          if (option && "properties" in option && "properties" in result) {
            mergePropertiesIntoResult(result.properties, option.properties);
          }
        }
      }
    } else if (key === "allOf") {
      continue;
    } else {
      if (override || result[key] === void 0) {
        result[key] = value;
      }
    }
  }
};
const mergePropertiesIntoResult = (result, properties) => {
  const propertyKeys = Object.keys(properties ?? {});
  if (!properties || !result || propertyKeys.length === 0) {
    return;
  }
  for (const key of propertyKeys) {
    const schema = resolve.schema(properties[key]);
    if (!schema) {
      delete result[key];
      continue;
    }
    if (typeof schema !== "object") {
      result[key] = schema;
      continue;
    }
    if (!result[key]) {
      if (schema.allOf) {
        result[key] = mergeAllOfSchemas(schema);
      } else if (isArraySchema(schema) && resolve.schema(schema.items)?.allOf) {
        result[key] = {
          ...schema,
          items: mergeAllOfSchemas(resolve.schema(schema.items))
        };
      } else if (properties[key]) {
        result[key] = properties[key];
      }
      continue;
    }
    const existing = resolve.schema(result[key]);
    if (schema.allOf) {
      result[key] = mergeAllOfSchemas({ allOf: [existing, ...schema.allOf] });
    } else if (isArraySchema(schema) && isArraySchema(existing) && schema.items) {
      const existingItems = resolve.schema(existing.items);
      result[key] = {
        ...existing,
        type: "array",
        items: existingItems ? mergeItems(existingItems, resolve.schema(schema.items)) : resolve.schema(schema.items)
      };
    } else {
      if ("properties" in existing && "properties" in schema) {
        const merged = { ...existing, ...schema };
        merged.properties = { ...existing.properties };
        mergePropertiesIntoResult(merged.properties, schema.properties);
        result[key] = merged;
      } else {
        result[key] = { ...schema, ...existing };
      }
    }
  }
};
const mergeItemsIntoResult = (result, items) => {
  if (items.allOf || result.allOf) {
    const allOfSchemas = [];
    if (result.allOf) {
      for (const schema of result.allOf) {
        allOfSchemas.push(resolve.schema(schema));
      }
    } else {
      allOfSchemas.push(result);
    }
    if (items.allOf) {
      for (const schema of items.allOf) {
        allOfSchemas.push(resolve.schema(schema));
      }
    } else {
      allOfSchemas.push(items);
    }
    const merged = mergeAllOfSchemas({ allOf: allOfSchemas });
    Object.assign(result, merged);
    return;
  }
  Object.assign(result, items);
  if ("properties" in result && "properties" in items) {
    mergePropertiesIntoResult(result.properties, items.properties);
  }
};
const mergeItems = (existing, incoming) => {
  if (existing.allOf || incoming.allOf) {
    const allOfSchemas = [];
    if (existing.allOf) {
      for (const schema of existing.allOf) {
        allOfSchemas.push(resolve.schema(schema));
      }
    } else {
      allOfSchemas.push(existing);
    }
    if (incoming.allOf) {
      for (const schema of incoming.allOf) {
        allOfSchemas.push(resolve.schema(schema));
      }
    } else {
      allOfSchemas.push(incoming);
    }
    return mergeAllOfSchemas({ allOf: allOfSchemas });
  }
  const merged = { ...existing, ...incoming };
  if ("properties" in existing && "properties" in incoming) {
    merged.properties = { ...existing.properties };
    mergePropertiesIntoResult(merged.properties, incoming.properties);
  }
  return merged;
};
export {
  mergeAllOfSchemas
};
