import { GraphQLInputObjectType, GraphQLBoolean, GraphQLList, GraphQLString, GraphQLNonNull, GraphQLFloat, GraphQLInt, GraphQLObjectType, GraphQLID, GraphQLScalarType, Kind, GraphQLEnumType, GraphQLSchema } from 'graphql';
import { bT as requirePermission, G as createContentManager, bU as isWorkflowEnabled, bV as getWorkflowConfig, bW as performTransition, a as getSchemaManager } from '../nitro/nitro.mjs';
import { a as getAuditActorFromContext } from './audit-log-actor.mjs';

const StringFilterInput = new GraphQLInputObjectType({
  name: "StringFilterInput",
  fields: {
    eq: { type: GraphQLString },
    ne: { type: GraphQLString },
    contains: { type: GraphQLString },
    startsWith: { type: GraphQLString },
    endsWith: { type: GraphQLString },
    in: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
    nin: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
const IntFilterInput = new GraphQLInputObjectType({
  name: "IntFilterInput",
  fields: {
    eq: { type: GraphQLInt },
    ne: { type: GraphQLInt },
    gt: { type: GraphQLInt },
    gte: { type: GraphQLInt },
    lt: { type: GraphQLInt },
    lte: { type: GraphQLInt },
    in: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) },
    nin: { type: new GraphQLList(new GraphQLNonNull(GraphQLInt)) },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
const FloatFilterInput = new GraphQLInputObjectType({
  name: "FloatFilterInput",
  fields: {
    eq: { type: GraphQLFloat },
    ne: { type: GraphQLFloat },
    gt: { type: GraphQLFloat },
    gte: { type: GraphQLFloat },
    lt: { type: GraphQLFloat },
    lte: { type: GraphQLFloat },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
const BooleanFilterInput = new GraphQLInputObjectType({
  name: "BooleanFilterInput",
  fields: {
    eq: { type: GraphQLBoolean },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
const DateTimeFilterInput = new GraphQLInputObjectType({
  name: "DateTimeFilterInput",
  fields: {
    eq: { type: GraphQLString },
    ne: { type: GraphQLString },
    gt: { type: GraphQLString },
    gte: { type: GraphQLString },
    lt: { type: GraphQLString },
    lte: { type: GraphQLString },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
const IDFilterInput = new GraphQLInputObjectType({
  name: "IDFilterInput",
  fields: {
    eq: { type: GraphQLString },
    ne: { type: GraphQLString },
    in: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
    nin: { type: new GraphQLList(new GraphQLNonNull(GraphQLString)) },
    null: { type: GraphQLBoolean },
    notNull: { type: GraphQLBoolean }
  }
});
function getFilterInputForFieldType(fieldType) {
  switch (fieldType) {
    case "string":
    case "text":
    case "richtext":
    case "enum":
      return StringFilterInput;
    case "integer":
    case "bigint":
      return IntFilterInput;
    case "decimal":
      return FloatFilterInput;
    case "boolean":
      return BooleanFilterInput;
    case "date":
    case "datetime":
      return DateTimeFilterInput;
    default:
      return null;
  }
}

function toPascalCase(str) {
  return str.split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
}
function toCamelCase(str) {
  const parts = str.split("-");
  return parts[0] + parts.slice(1).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
}
function sanitizeGraphQLName(name) {
  return name.replace(/[^a-zA-Z0-9_]/g, "_");
}
function getSourceField(source, ...keys) {
  if (!source || typeof source !== "object") {
    return void 0;
  }
  const record = source;
  for (const key of keys) {
    if (key in record) {
      return record[key];
    }
  }
  return void 0;
}
function resolvePublishedState(source) {
  const explicitValue = getSourceField(source, "published");
  if (typeof explicitValue === "boolean") {
    return explicitValue;
  }
  const publishedAtValue = getSourceField(source, "publishedAt", "published_at");
  if (publishedAtValue === void 0 || publishedAtValue === null) {
    return false;
  }
  const publishedAt = new Date(String(publishedAtValue));
  if (Number.isNaN(publishedAt.getTime())) {
    return false;
  }
  return publishedAt <= /* @__PURE__ */ new Date();
}
function getFieldNames(selectionSet) {
  if (!selectionSet) {
    return [];
  }
  const names = [];
  for (const selection of selectionSet.selections) {
    if (selection.kind === "Field") {
      names.push(selection.name.value);
    }
  }
  return names;
}
function getPopulateFromInfo(info, schema, dataFieldName) {
  var _a;
  const topSelection = info.fieldNodes[0];
  if (!(topSelection == null ? void 0 : topSelection.selectionSet)) {
    return [];
  }
  let targetSelectionSet;
  if (dataFieldName) {
    const dataField = topSelection.selectionSet.selections.find(
      (s) => s.kind === "Field" && s.name.value === dataFieldName
    );
    targetSelectionSet = dataField == null ? void 0 : dataField.selectionSet;
  } else {
    const dataField = topSelection.selectionSet.selections.find(
      (s) => s.kind === "Field" && s.name.value === "data"
    );
    targetSelectionSet = (_a = dataField == null ? void 0 : dataField.selectionSet) != null ? _a : topSelection.selectionSet;
  }
  if (!targetSelectionSet) {
    return [];
  }
  const requestedFields = getFieldNames(targetSelectionSet);
  const populateFields = [];
  for (const fieldName of requestedFields) {
    const fieldDef = schema.attributes[fieldName];
    if (!fieldDef) {
      continue;
    }
    if (fieldDef.type === "relation" || fieldDef.type === "media") {
      populateFields.push(fieldName);
    }
  }
  return populateFields;
}
const SENSITIVE_FIELDS = /* @__PURE__ */ new Set(["password"]);
const SYSTEM_SCHEMAS = /* @__PURE__ */ new Set(["user", "media", "media-folder", "role"]);
const EXCLUDED_SCHEMAS = /* @__PURE__ */ new Set(["media-folder"]);
function convertGraphQLFilters(filters) {
  if (!filters) {
    return {};
  }
  const result = {};
  for (const [field, value] of Object.entries(filters)) {
    if (value === void 0 || value === null) {
      continue;
    }
    if (typeof value === "object" && !Array.isArray(value)) {
      const filterObj = value;
      const hasOperators = Object.keys(filterObj).some(
        (k) => [
          "eq",
          "ne",
          "gt",
          "gte",
          "lt",
          "lte",
          "contains",
          "startsWith",
          "endsWith",
          "in",
          "nin",
          "null",
          "notNull"
        ].includes(k)
      );
      if (hasOperators) {
        const operatorFilter = {};
        for (const [op, opValue] of Object.entries(filterObj)) {
          if (opValue === void 0 || opValue === null) {
            continue;
          }
          if (op === "null") {
            if (opValue === true) {
              operatorFilter["$null"] = true;
            }
          } else if (op === "notNull") {
            if (opValue === true) {
              operatorFilter["$notNull"] = true;
            }
          } else {
            operatorFilter[`$${op}`] = opValue;
          }
        }
        result[field] = operatorFilter;
      } else {
        result[field] = convertGraphQLFilters(filterObj);
      }
    } else {
      result[field] = value;
    }
  }
  return result;
}

const GraphQLJSON = new GraphQLScalarType({
  name: "JSON",
  description: "Arbitrary JSON value",
  serialize: (value) => value,
  parseValue: (value) => value,
  parseLiteral: (ast) => parseLiteralToJSON(ast)
});
function parseLiteralToJSON(ast) {
  var _a, _b;
  switch (ast.kind) {
    case Kind.STRING:
    case Kind.BOOLEAN:
      return ast.value;
    case Kind.INT:
      return parseInt(ast.value, 10);
    case Kind.FLOAT:
      return parseFloat(ast.value);
    case Kind.OBJECT: {
      const obj = {};
      for (const field of (_a = ast.fields) != null ? _a : []) {
        obj[field.name.value] = parseLiteralToJSON(field.value);
      }
      return obj;
    }
    case Kind.LIST:
      return ((_b = ast.values) != null ? _b : []).map(parseLiteralToJSON);
    case Kind.NULL:
      return null;
    default:
      return null;
  }
}
const GraphQLDateTime = new GraphQLScalarType({
  name: "DateTime",
  description: "ISO 8601 date-time string",
  serialize: (value) => {
    if (value instanceof Date) {
      return value.toISOString();
    }
    return String(value);
  },
  parseValue: (value) => String(value),
  parseLiteral: (ast) => ast.kind === Kind.STRING ? ast.value : null
});
const SortOrderEnum = new GraphQLEnumType({
  name: "SortOrder",
  values: {
    asc: { value: "asc" },
    desc: { value: "desc" }
  }
});
const DeleteResponseType = new GraphQLObjectType({
  name: "DeleteResponse",
  fields: {
    success: { type: new GraphQLNonNull(GraphQLBoolean) },
    id: { type: new GraphQLNonNull(GraphQLID) }
  }
});
function createTypeRegistry() {
  return {
    objectTypes: /* @__PURE__ */ new Map(),
    inputTypes: /* @__PURE__ */ new Map(),
    filterTypes: /* @__PURE__ */ new Map(),
    listResponseTypes: /* @__PURE__ */ new Map(),
    enumTypes: /* @__PURE__ */ new Map()
  };
}
function getGraphQLOutputType(field, fieldName, schemaName, registry) {
  switch (field.type) {
    case "string":
    case "text":
    case "richtext":
      return GraphQLString;
    case "integer":
      return GraphQLInt;
    case "bigint":
      return GraphQLString;
    // BigInt as string to preserve precision
    case "decimal":
      return GraphQLFloat;
    case "boolean":
      return GraphQLBoolean;
    case "date":
    case "datetime":
      return GraphQLDateTime;
    case "json":
      return GraphQLJSON;
    case "enum": {
      if (field.enum && field.enum.length > 0) {
        const enumName = sanitizeGraphQLName(
          `${toPascalCase(schemaName)}_${toPascalCase(fieldName)}`
        );
        const existing = registry.enumTypes.get(enumName);
        if (existing) {
          return existing;
        }
        const values = {};
        for (const val of field.enum) {
          const safeName = sanitizeGraphQLName(val) || `VALUE_${Object.keys(values).length}`;
          values[safeName] = { value: val };
        }
        const enumType = new GraphQLEnumType({
          name: enumName,
          values
        });
        registry.enumTypes.set(enumName, enumType);
        return enumType;
      }
      return GraphQLString;
    }
    case "relation": {
      return null;
    }
    case "media": {
      return null;
    }
    case "component":
    case "dynamicZone":
      return GraphQLJSON;
    // Components as JSON for now
    default:
      return GraphQLString;
  }
}
function getGraphQLInputType(field) {
  switch (field.type) {
    case "string":
    case "text":
    case "richtext":
      return GraphQLString;
    case "integer":
      return GraphQLInt;
    case "bigint":
      return GraphQLString;
    case "decimal":
      return GraphQLFloat;
    case "boolean":
      return GraphQLBoolean;
    case "date":
    case "datetime":
      return GraphQLString;
    // ISO string input
    case "json":
      return GraphQLJSON;
    case "enum":
      return GraphQLString;
    // Accept string values for enum inputs
    case "relation":
      if (field.relation === "manyToMany" || field.relation === "oneToMany") {
        return new GraphQLList(new GraphQLNonNull(GraphQLID));
      }
      return GraphQLID;
    case "media":
      if (field.multiple) {
        return new GraphQLList(new GraphQLNonNull(GraphQLID));
      }
      return GraphQLID;
    case "component":
    case "dynamicZone":
      return GraphQLJSON;
    default:
      return GraphQLString;
  }
}
function buildContentObjectType(schema, registry) {
  const typeName = toPascalCase(schema.info.singularName);
  const existing = registry.objectTypes.get(typeName);
  if (existing) {
    return existing;
  }
  const objectType = new GraphQLObjectType({
    name: typeName,
    description: schema.info.description,
    fields: () => {
      var _a, _b, _c, _d, _e, _f;
      const fields = {
        id: { type: new GraphQLNonNull(GraphQLID) }
      };
      if ((_a = schema.options) == null ? void 0 : _a.timestamps) {
        fields.createdAt = {
          type: GraphQLDateTime,
          resolve: (source) => {
            var _a2;
            return (_a2 = getSourceField(source, "createdAt", "created_at")) != null ? _a2 : null;
          }
        };
        fields.updatedAt = {
          type: GraphQLDateTime,
          resolve: (source) => {
            var _a2;
            return (_a2 = getSourceField(source, "updatedAt", "updated_at")) != null ? _a2 : null;
          }
        };
      }
      if ((_b = schema.options) == null ? void 0 : _b.draftAndPublish) {
        fields.publishedAt = {
          type: GraphQLDateTime,
          resolve: (source) => {
            var _a2;
            return (_a2 = getSourceField(source, "publishedAt", "published_at")) != null ? _a2 : null;
          }
        };
        fields.published = {
          type: GraphQLBoolean,
          resolve: (source) => resolvePublishedState(source)
        };
      }
      if ((_d = (_c = schema.options) == null ? void 0 : _c.i18n) == null ? void 0 : _d.enabled) {
        fields.locale = { type: GraphQLString };
        fields.documentId = {
          type: GraphQLString,
          resolve: (source) => {
            var _a2;
            return (_a2 = getSourceField(source, "documentId", "document_id")) != null ? _a2 : null;
          }
        };
      }
      for (const [fieldName, fieldDef] of Object.entries(schema.attributes)) {
        if (SENSITIVE_FIELDS.has(fieldName)) {
          continue;
        }
        if (fieldDef.type === "relation") {
          const targetName = fieldDef.target;
          if (!targetName) {
            continue;
          }
          const targetTypeName = toPascalCase(targetName);
          const isToMany = fieldDef.relation === "oneToMany" || fieldDef.relation === "manyToMany";
          if (isToMany) {
            fields[fieldName] = {
              type: new GraphQLList(
                new GraphQLNonNull(
                  (_e = registry.objectTypes.get(targetTypeName)) != null ? _e : buildPlaceholderType(targetTypeName)
                )
              )
            };
          } else {
            fields[fieldName] = {
              type: (_f = registry.objectTypes.get(targetTypeName)) != null ? _f : buildPlaceholderType(targetTypeName)
            };
          }
        } else if (fieldDef.type === "media") {
          const mediaType = registry.objectTypes.get("Media");
          if (mediaType) {
            if (fieldDef.multiple) {
              fields[fieldName] = {
                type: new GraphQLList(new GraphQLNonNull(mediaType))
              };
            } else {
              fields[fieldName] = { type: mediaType };
            }
          } else {
            fields[fieldName] = { type: GraphQLJSON };
          }
        } else {
          const graphqlType = getGraphQLOutputType(
            fieldDef,
            fieldName,
            schema.info.singularName,
            registry
          );
          if (graphqlType) {
            if (fieldDef.required) {
              fields[fieldName] = { type: new GraphQLNonNull(graphqlType) };
            } else {
              fields[fieldName] = { type: graphqlType };
            }
          }
        }
      }
      return fields;
    }
  });
  registry.objectTypes.set(typeName, objectType);
  return objectType;
}
function buildPlaceholderType(name) {
  return new GraphQLObjectType({
    name: `_Placeholder_${name}`,
    fields: { id: { type: GraphQLID } }
  });
}
function buildContentInputType(schema, registry) {
  var _a, _b;
  const typeName = `${toPascalCase(schema.info.singularName)}Input`;
  const existing = registry.inputTypes.get(typeName);
  if (existing) {
    return existing;
  }
  const fields = {};
  for (const [fieldName, fieldDef] of Object.entries(schema.attributes)) {
    if (SENSITIVE_FIELDS.has(fieldName)) {
      continue;
    }
    if (fieldDef.type === "relation" && fieldDef.relation === "oneToMany") {
      continue;
    }
    const inputType2 = getGraphQLInputType(fieldDef);
    if (inputType2) {
      fields[fieldName] = { type: inputType2 };
    }
  }
  if ((_b = (_a = schema.options) == null ? void 0 : _a.i18n) == null ? void 0 : _b.enabled) {
    fields.locale = { type: GraphQLString };
  }
  const inputType = new GraphQLInputObjectType({
    name: typeName,
    fields
  });
  registry.inputTypes.set(typeName, inputType);
  return inputType;
}
function buildContentFilterType(schema, registry) {
  var _a;
  const typeName = `${toPascalCase(schema.info.singularName)}Filters`;
  const existing = registry.filterTypes.get(typeName);
  if (existing) {
    return existing;
  }
  const fields = {};
  fields.id = { type: IDFilterInput };
  for (const [fieldName, fieldDef] of Object.entries(schema.attributes)) {
    if (SENSITIVE_FIELDS.has(fieldName)) {
      continue;
    }
    if (fieldDef.type === "relation") {
      if (fieldDef.target && (fieldDef.relation === "manyToOne" || fieldDef.relation === "oneToOne")) {
        const targetTypeName = `${toPascalCase(fieldDef.target)}Filters`;
        const targetFilter = registry.filterTypes.get(targetTypeName);
        if (targetFilter) {
          fields[fieldName] = { type: targetFilter };
        } else {
          fields[fieldName] = { type: IDFilterInput };
        }
      }
      continue;
    }
    const filterInput = getFilterInputForFieldType(fieldDef.type);
    if (filterInput) {
      fields[fieldName] = { type: filterInput };
    }
  }
  if ((_a = schema.options) == null ? void 0 : _a.draftAndPublish) {
    fields.published = { type: GraphQLBoolean };
  }
  const filterType = new GraphQLInputObjectType({
    name: typeName,
    fields
  });
  registry.filterTypes.set(typeName, filterType);
  return filterType;
}
function buildListResponseType(schema, objectType, registry) {
  const typeName = `${toPascalCase(schema.info.singularName)}ListResponse`;
  const existing = registry.listResponseTypes.get(typeName);
  if (existing) {
    return existing;
  }
  const listType = new GraphQLObjectType({
    name: typeName,
    fields: {
      data: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(objectType))) },
      total: { type: new GraphQLNonNull(GraphQLInt) },
      limit: { type: new GraphQLNonNull(GraphQLInt) },
      offset: { type: new GraphQLNonNull(GraphQLInt) }
    }
  });
  registry.listResponseTypes.set(typeName, listType);
  return listType;
}

function normalizeReferenceValue(value) {
  if (Array.isArray(value)) {
    return value.map((item) => normalizeReferenceValue(item));
  }
  if (typeof value === "string" && /^\d+$/.test(value)) {
    return Number.parseInt(value, 10);
  }
  if (value && typeof value === "object" && "id" in value) {
    const record = value;
    return {
      ...record,
      id: normalizeReferenceValue(record.id)
    };
  }
  return value;
}
function normalizeMutationInput(schema, data) {
  const normalizedData = {
    ...data
  };
  for (const [fieldName, fieldDef] of Object.entries(schema.attributes)) {
    if (!(fieldName in normalizedData)) {
      continue;
    }
    if (fieldDef.type === "relation" || fieldDef.type === "media") {
      normalizedData[fieldName] = normalizeReferenceValue(normalizedData[fieldName]);
    }
  }
  return normalizedData;
}
async function populateSelectionResult(contentManager, schema, info, result) {
  const populateFields = getPopulateFromInfo(info, schema);
  if (populateFields.length === 0) {
    return result;
  }
  return await contentManager.populateRecords(result, populateFields);
}
function createListQueryResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const pluralName = schema.info.pluralName;
  const typeName = toPascalCase(singularName);
  const listResponseType = registry.listResponseTypes.get(`${typeName}ListResponse`);
  const filterType = registry.filterTypes.get(`${typeName}Filters`);
  if (!listResponseType) {
    throw new Error(`List response type not found for ${typeName}`);
  }
  return {
    type: new GraphQLNonNull(listResponseType),
    description: `List ${pluralName} with filtering, sorting, and pagination`,
    args: {
      ...filterType ? { filters: { type: filterType } } : {},
      sort: { type: GraphQLString, defaultValue: "id" },
      sortOrder: { type: SortOrderEnum, defaultValue: "desc" },
      limit: { type: GraphQLInt, defaultValue: 25 },
      offset: { type: GraphQLInt, defaultValue: 0 },
      search: { type: GraphQLString },
      published: { type: GraphQLBoolean },
      locale: { type: GraphQLString }
    },
    resolve: async (_root, args, ctx, info) => {
      var _a, _b, _c, _d, _e, _f, _g;
      await requirePermission(ctx.user, singularName, "read", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const result = await contentManager.findMany({
        limit: (_a = args.limit) != null ? _a : 25,
        offset: (_b = args.offset) != null ? _b : 0,
        sort: (_c = args.sort) != null ? _c : "id",
        sortOrder: (_d = args.sortOrder) != null ? _d : "desc",
        filters: convertGraphQLFilters(args.filters),
        search: (_e = args.search) != null ? _e : void 0,
        published: (_f = args.published) != null ? _f : void 0,
        locale: (_g = args.locale) != null ? _g : void 0
      });
      if (result.data.length > 0) {
        const populateFields = getPopulateFromInfo(info, schema, "data");
        if (populateFields.length > 0) {
          result.data = await contentManager.populateRecords(
            result.data,
            populateFields
          );
        }
      }
      return result;
    }
  };
}
function createFindOneQueryResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const typeName = toPascalCase(singularName);
  const objectType = registry.objectTypes.get(typeName);
  if (!objectType) {
    throw new Error(`Object type not found for ${typeName}`);
  }
  return {
    type: objectType,
    description: `Get a single ${singularName} by ID`,
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) }
    },
    resolve: async (_root, args, ctx, info) => {
      await requirePermission(ctx.user, singularName, "read", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      let result = await contentManager.findOne(parseInt(args.id, 10));
      if (!result) {
        return null;
      }
      const populateFields = getPopulateFromInfo(info, schema);
      if (populateFields.length > 0) {
        result = await contentManager.populateRecords(result, populateFields);
      }
      return result;
    }
  };
}
function createCreateMutationResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const typeName = toPascalCase(singularName);
  const objectType = registry.objectTypes.get(typeName);
  const inputType = registry.inputTypes.get(`${typeName}Input`);
  if (!objectType || !inputType) {
    throw new Error(`Types not found for ${typeName}`);
  }
  return {
    type: new GraphQLNonNull(objectType),
    description: `Create a new ${singularName}`,
    args: {
      data: { type: new GraphQLNonNull(inputType) }
    },
    resolve: async (_root, args, ctx, info) => {
      await requirePermission(ctx.user, singularName, "create", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const normalizedData = normalizeMutationInput(schema, args.data);
      const result = await contentManager.create(normalizedData, {
        actor: getAuditActorFromContext(ctx)
      });
      return populateSelectionResult(contentManager, schema, info, result);
    }
  };
}
function createUpdateMutationResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const typeName = toPascalCase(singularName);
  const objectType = registry.objectTypes.get(typeName);
  const inputType = registry.inputTypes.get(`${typeName}Input`);
  if (!objectType || !inputType) {
    throw new Error(`Types not found for ${typeName}`);
  }
  return {
    type: new GraphQLNonNull(objectType),
    description: `Update an existing ${singularName}`,
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) },
      data: { type: new GraphQLNonNull(inputType) }
    },
    resolve: async (_root, args, ctx, info) => {
      await requirePermission(ctx.user, singularName, "update", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const normalizedData = normalizeMutationInput(schema, args.data);
      const result = await contentManager.update(parseInt(args.id, 10), normalizedData, void 0, {
        actor: getAuditActorFromContext(ctx)
      });
      return populateSelectionResult(contentManager, schema, info, result);
    }
  };
}
function createDeleteMutationResolver(schema) {
  const singularName = schema.info.singularName;
  return {
    type: new GraphQLNonNull(DeleteResponseType),
    description: `Delete a ${singularName}`,
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) }
    },
    resolve: async (_root, args, ctx) => {
      await requirePermission(ctx.user, singularName, "delete", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const numericId = parseInt(args.id, 10);
      await contentManager.delete(numericId, {
        actor: getAuditActorFromContext(ctx)
      });
      return { success: true, id: args.id };
    }
  };
}
function createPublishMutationResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const typeName = toPascalCase(singularName);
  const objectType = registry.objectTypes.get(typeName);
  if (!objectType) {
    throw new Error(`Object type not found for ${typeName}`);
  }
  return {
    type: new GraphQLNonNull(objectType),
    description: `Publish a ${singularName} (set publishedAt to now)`,
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) },
      publishedAt: {
        type: GraphQLString,
        description: "Optional ISO date for scheduled publishing"
      }
    },
    resolve: async (_root, args, ctx, info) => {
      var _a, _b, _c, _d, _e;
      await requirePermission(ctx.user, singularName, "publish", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const numericId = parseInt(args.id, 10);
      if (isWorkflowEnabled(singularName)) {
        const config = getWorkflowConfig(singularName);
        const publishableState = config == null ? void 0 : config.states.find((state) => state.publishable);
        if (!publishableState) {
          throw new Error("No publishable state defined in workflow configuration");
        }
        const actorId = (_c = (_a = ctx.adminUser) == null ? void 0 : _a.id) != null ? _c : (_b = ctx.user) == null ? void 0 : _b.id;
        if (!actorId) {
          throw new Error("Authentication required for workflow transitions");
        }
        const actorRole = ((_d = ctx.adminUser) == null ? void 0 : _d.role) || ((_e = ctx.user) == null ? void 0 : _e.role);
        const transitionResult = await performTransition(
          singularName,
          numericId,
          publishableState.name,
          actorId,
          actorRole
        );
        if (!transitionResult.success) {
          throw new Error(transitionResult.error || "Cannot publish via workflow");
        }
        if (transitionResult.pendingApproval) {
          const currentEntry = await contentManager.findOne(numericId);
          if (!currentEntry) {
            throw new Error(`Record with id ${numericId} not found`);
          }
          return populateSelectionResult(contentManager, schema, info, currentEntry);
        }
      }
      const publishedAt = args.publishedAt ? new Date(args.publishedAt).toISOString() : (/* @__PURE__ */ new Date()).toISOString();
      const result = await contentManager.update(
        numericId,
        { published_at: publishedAt },
        "publish",
        { actor: getAuditActorFromContext(ctx) }
      );
      return populateSelectionResult(contentManager, schema, info, result);
    }
  };
}
function createUnpublishMutationResolver(schema, registry) {
  const singularName = schema.info.singularName;
  const typeName = toPascalCase(singularName);
  const objectType = registry.objectTypes.get(typeName);
  if (!objectType) {
    throw new Error(`Object type not found for ${typeName}`);
  }
  return {
    type: new GraphQLNonNull(objectType),
    description: `Unpublish a ${singularName} (set publishedAt to null)`,
    args: {
      id: { type: new GraphQLNonNull(GraphQLID) }
    },
    resolve: async (_root, args, ctx, info) => {
      var _a, _b, _c, _d, _e;
      await requirePermission(ctx.user, singularName, "publish", ctx.adminUser, ctx.apiToken);
      const contentManager = createContentManager(singularName);
      const numericId = parseInt(args.id, 10);
      if (isWorkflowEnabled(singularName)) {
        const config = getWorkflowConfig(singularName);
        if (!config) {
          throw new Error("Workflow configuration error");
        }
        const actorId = (_c = (_a = ctx.adminUser) == null ? void 0 : _a.id) != null ? _c : (_b = ctx.user) == null ? void 0 : _b.id;
        if (!actorId) {
          throw new Error("Authentication required for workflow transitions");
        }
        const actorRole = ((_d = ctx.adminUser) == null ? void 0 : _d.role) || ((_e = ctx.user) == null ? void 0 : _e.role);
        const transitionResult = await performTransition(
          singularName,
          numericId,
          config.initialState,
          actorId,
          actorRole
        );
        if (!transitionResult.success) {
          throw new Error(transitionResult.error || "Cannot unpublish via workflow");
        }
        if (transitionResult.pendingApproval) {
          const currentEntry = await contentManager.findOne(numericId);
          if (!currentEntry) {
            throw new Error(`Record with id ${numericId} not found`);
          }
          return populateSelectionResult(contentManager, schema, info, currentEntry);
        }
      }
      const result = await contentManager.update(numericId, { published_at: null }, "unpublish", {
        actor: getAuditActorFromContext(ctx)
      });
      return populateSelectionResult(contentManager, schema, info, result);
    }
  };
}
function buildResolversForSchema(schema, registry) {
  var _a;
  const singularName = schema.info.singularName;
  const pluralCamelCase = toCamelCase(schema.info.pluralName);
  const singularCamelCase = toCamelCase(singularName);
  const pascalName = toPascalCase(singularName);
  const isSystem = SYSTEM_SCHEMAS.has(singularName);
  const queries = {};
  const mutations = {};
  if (schema.kind === "collectionType") {
    queries[pluralCamelCase] = createListQueryResolver(schema, registry);
  }
  queries[singularCamelCase] = createFindOneQueryResolver(schema, registry);
  if (!isSystem) {
    mutations[`create${pascalName}`] = createCreateMutationResolver(schema, registry);
    mutations[`update${pascalName}`] = createUpdateMutationResolver(schema, registry);
    mutations[`delete${pascalName}`] = createDeleteMutationResolver(schema);
    if ((_a = schema.options) == null ? void 0 : _a.draftAndPublish) {
      mutations[`publish${pascalName}`] = createPublishMutationResolver(schema, registry);
      mutations[`unpublish${pascalName}`] = createUnpublishMutationResolver(schema, registry);
    }
  }
  return { queries, mutations };
}

function buildGraphQLSchema() {
  const schemaManager = getSchemaManager();
  const allSchemas = schemaManager.getAllSchemas();
  const registry = createTypeRegistry();
  const schemas = allSchemas.filter((s) => !EXCLUDED_SCHEMAS.has(s.info.singularName));
  for (const schema of schemas) {
    buildContentObjectType(schema, registry);
  }
  for (const schema of schemas) {
    buildContentFilterType(schema, registry);
  }
  for (const schema of schemas) {
    const objectType = registry.objectTypes.get(toPascalCase(schema.info.singularName));
    if (!objectType) {
      continue;
    }
    buildContentInputType(schema, registry);
    buildListResponseType(schema, objectType, registry);
  }
  const allQueries = {};
  const allMutations = {};
  for (const schema of schemas) {
    const { queries, mutations } = buildResolversForSchema(schema, registry);
    Object.assign(allQueries, queries);
    Object.assign(allMutations, mutations);
  }
  allQueries._schema = {
    type: new GraphQLNonNull(SchemaMetaType),
    description: "GraphQL schema metadata",
    resolve: () => ({
      version: "1.0.0",
      contentTypes: schemas.filter((s) => s.kind === "collectionType").map((s) => s.info.singularName),
      singleTypes: schemas.filter((s) => s.kind === "singleType").map((s) => s.info.singularName)
    })
  };
  const queryType = new GraphQLObjectType({
    name: "Query",
    fields: allQueries
  });
  const hasMutations = Object.keys(allMutations).length > 0;
  const mutationType = hasMutations ? new GraphQLObjectType({
    name: "Mutation",
    fields: allMutations
  }) : void 0;
  return new GraphQLSchema({
    query: queryType,
    mutation: mutationType
  });
}
const SchemaMetaType = new GraphQLObjectType({
  name: "SchemaMeta",
  description: "Notty CMS GraphQL schema metadata",
  fields: {
    version: { type: new GraphQLNonNull(GraphQLString) },
    contentTypes: {
      type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))),
      description: "Available collection content types"
    },
    singleTypes: {
      type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))),
      description: "Available single content types"
    }
  }
});
let cachedSchema = null;
function getGraphQLSchema() {
  if (!cachedSchema) {
    cachedSchema = buildGraphQLSchema();
    console.log("\u2705 GraphQL schema built successfully");
  }
  return cachedSchema;
}
function invalidateGraphQLSchema() {
  cachedSchema = null;
}

export { getGraphQLSchema as g, invalidateGraphQLSchema as i };
//# sourceMappingURL=schema-builder.mjs.map
