import { f as defineAdminEventHandler, h as requireAdminRole, q as readBody, z as withAIGuardrails, B as approxBodyBytes, a as getSchemaManager, C as getEnabledContentLocales, D as generateAutomationSuggestions, b as getDatabaseManager, E as dbQuery, F as DEFAULT_AUTOMATION_THRESHOLDS } from '../../../../nitro/nitro.mjs';
import 'node:crypto';
import 'jsonwebtoken';
import 'node:fs';
import 'node:module';
import 'node:path';
import 'node:url';
import 'node:http';
import 'node:https';
import 'node:events';
import 'node:buffer';
import 'pino';
import 'drizzle-orm/better-sqlite3';
import 'drizzle-orm/node-postgres';
import 'drizzle-orm/mysql2';
import 'better-sqlite3';
import 'pg';
import 'mysql2/promise';
import 'drizzle-orm';
import 'fs';
import 'path';
import 'prom-client';
import 'bcryptjs';
import '@node-rs/argon2';
import 'node:async_hooks';
import 'crypto';
import 'node:dns';
import 'node:util';
import 'node:net';
import 'fs/promises';
import 'url';
import 'semver';

const SUGGESTION_CATEGORIES = [
  "stale-draft",
  "stale-content",
  "workflow",
  "i18n",
  "media",
  "housekeeping",
  "bulk"
];
const HEAVY_ASSET_BYTES = 15e5;
const SAMPLE_SIZE = 10;
const SCHEMAS_TO_SKIP = /* @__PURE__ */ new Set(["user", "role", "media", "media-folder", "api-token"]);
const automationAdvisor_post = defineAdminEventHandler("admin:ai:automation-advisor", async (event) => {
  var _a, _b, _c, _d, _e, _f;
  await requireAdminRole(event, ["super-admin", "admin"]);
  const body = (_a = await readBody(event)) != null ? _a : {};
  const request = parseRequest(body);
  const thresholds = {
    ...DEFAULT_AUTOMATION_THRESHOLDS,
    ...(_b = request.thresholds) != null ? _b : {}
  };
  const now = /* @__PURE__ */ new Date();
  const data = await withAIGuardrails({
    advisor: "automation",
    event,
    bodyBytes: approxBodyBytes(body),
    inputDigest: {
      hasIntent: Boolean(request.intent),
      scopeSchemas: (_d = (_c = request.scopeSchemas) == null ? void 0 : _c.length) != null ? _d : 0,
      excludeCategories: (_f = (_e = request.excludeCategories) == null ? void 0 : _e.length) != null ? _f : 0
    },
    run: async () => {
      const schemas = getSchemaManager().getAllSchemas();
      const profileSchemas = schemas.filter(
        (schema) => !SCHEMAS_TO_SKIP.has(schema.info.singularName)
      );
      const contentStats = [];
      for (const schema of profileSchemas) {
        const stats = await collectContentStats(schema, thresholds, now);
        if (stats) {
          contentStats.push(stats);
        }
      }
      const mediaStats = await collectMediaStats(schemas, thresholds);
      const enabledLocales = await getEnabledContentLocales().catch(() => []);
      const availableLocales = enabledLocales.map((locale) => locale.code);
      return generateAutomationSuggestions(request, {
        schemas,
        contentStats,
        mediaStats,
        now,
        availableLocales,
        thresholds
      });
    },
    digestOutput: (result) => ({
      suggestions: result.suggestions.length,
      bySeverity: {
        critical: result.summary.critical,
        warning: result.summary.warning,
        info: result.summary.info
      }
    })
  });
  return { success: true, data };
});
function parseRequest(body) {
  const request = {};
  if (Array.isArray(body.scopeSchemas)) {
    const list = body.scopeSchemas.filter(
      (value) => typeof value === "string" && value.length > 0
    );
    if (list.length > 0) {
      request.scopeSchemas = list;
    }
  }
  if (Array.isArray(body.excludeCategories)) {
    const list = body.excludeCategories.filter(
      (value) => SUGGESTION_CATEGORIES.includes(value)
    );
    if (list.length > 0) {
      request.excludeCategories = list;
    }
  }
  if (body.thresholds && typeof body.thresholds === "object" && !Array.isArray(body.thresholds)) {
    const raw = body.thresholds;
    const parsed = {};
    const numericKeys = [
      "abandonedDraftDays",
      "staleContentDays",
      "workflowStuckDays",
      "orphanMediaDays",
      "bulkRecommendationMinCount"
    ];
    for (const key of numericKeys) {
      const value = raw[key];
      const parsedValue = parsePositiveInt(value);
      if (parsedValue !== void 0) {
        parsed[key] = parsedValue;
      }
    }
    if (Object.keys(parsed).length > 0) {
      request.thresholds = parsed;
    }
  }
  if (typeof body.intent === "string" && body.intent.trim().length > 0) {
    request.intent = body.intent.trim();
  }
  return request;
}
function parsePositiveInt(value) {
  if (typeof value === "number" && Number.isFinite(value) && value > 0) {
    return Math.floor(value);
  }
  if (typeof value === "string" && value.trim().length > 0) {
    const parsed = Number(value);
    if (Number.isFinite(parsed) && parsed > 0) {
      return Math.floor(parsed);
    }
  }
  return void 0;
}
function tableNameOf(schema) {
  return schema.info.pluralName.toLowerCase().replace(/-/g, "_");
}
function quoteIdent(name) {
  if (!/^[a-z_][a-z0-9_]*$/i.test(name)) {
    throw new Error(`Unsafe identifier: ${name}`);
  }
  const dialect = getDatabaseManager().getDialect();
  return dialect === "mysql" ? `\`${name}\`` : `"${name}"`;
}
function thresholdDate(now, days) {
  const t = new Date(now.getTime() - days * 24 * 60 * 60 * 1e3);
  return t.toISOString().slice(0, 19).replace("T", " ");
}
async function safeQuery(sql, params) {
  try {
    return await dbQuery(sql, params);
  } catch {
    return [];
  }
}
async function tableExists(table) {
  var _a, _b, _c;
  const dialect = getDatabaseManager().getDialect();
  try {
    if (dialect === "sqlite") {
      const rows = await dbQuery(
        `SELECT name FROM sqlite_master WHERE type='table' AND name = ?`,
        [table]
      );
      return rows.length > 0;
    }
    if (dialect === "postgres") {
      const rows = await dbQuery(
        `SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = ?) AS exists`,
        [table]
      );
      return ((_a = rows[0]) == null ? void 0 : _a.exists) === true;
    }
    if (dialect === "mysql") {
      const rows = await dbQuery(
        `SELECT COUNT(*) AS cnt FROM information_schema.tables WHERE table_name = ? AND table_schema = DATABASE()`,
        [table]
      );
      return Number((_c = (_b = rows[0]) == null ? void 0 : _b.cnt) != null ? _c : 0) > 0;
    }
  } catch {
    return false;
  }
  return false;
}
async function columnExists(table, column) {
  var _a, _b, _c;
  const dialect = getDatabaseManager().getDialect();
  try {
    if (dialect === "sqlite") {
      const rows = await dbQuery(`PRAGMA table_info(${quoteIdent(table)})`, []);
      return rows.some((row) => row.name === column);
    }
    if (dialect === "postgres") {
      const rows = await dbQuery(
        `SELECT EXISTS (
           SELECT 1 FROM information_schema.columns
           WHERE table_name = ? AND column_name = ?
         ) AS exists`,
        [table, column]
      );
      return ((_a = rows[0]) == null ? void 0 : _a.exists) === true;
    }
    if (dialect === "mysql") {
      const rows = await dbQuery(
        `SELECT COUNT(*) AS cnt FROM information_schema.columns
         WHERE table_name = ? AND column_name = ? AND table_schema = DATABASE()`,
        [table, column]
      );
      return Number((_c = (_b = rows[0]) == null ? void 0 : _b.cnt) != null ? _c : 0) > 0;
    }
  } catch {
    return false;
  }
  return false;
}
async function collectContentStats(schema, thresholds, now) {
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
  const table = tableNameOf(schema);
  if (!await tableExists(table)) {
    return null;
  }
  const quoted = quoteIdent(table);
  const draftAndPublish = ((_a = schema.options) == null ? void 0 : _a.draftAndPublish) === true;
  const hasWorkflow = ((_b = schema.options) == null ? void 0 : _b.workflow) !== void 0;
  const hasUpdatedAt = await columnExists(table, "updated_at");
  const stats = {
    schemaName: schema.info.singularName,
    totalCount: 0,
    draftCount: 0,
    publishedCount: 0,
    abandonedDraftCount: 0,
    staleContentCount: 0,
    workflowStuckCount: 0,
    missingTranslationsCount: 0
  };
  const totalRows = await safeQuery(`SELECT COUNT(*) AS cnt FROM ${quoted}`, []);
  stats.totalCount = Number((_d = (_c = totalRows[0]) == null ? void 0 : _c.cnt) != null ? _d : 0);
  if (draftAndPublish && await columnExists(table, "published_at")) {
    const draftRows = await safeQuery(
      `SELECT COUNT(*) AS cnt FROM ${quoted} WHERE published_at IS NULL`,
      []
    );
    stats.draftCount = Number((_f = (_e = draftRows[0]) == null ? void 0 : _e.cnt) != null ? _f : 0);
    stats.publishedCount = stats.totalCount - stats.draftCount;
    if (hasUpdatedAt) {
      const cutoff = thresholdDate(now, thresholds.abandonedDraftDays);
      const abandonedRows = await safeQuery(
        `SELECT COUNT(*) AS cnt FROM ${quoted}
         WHERE published_at IS NULL AND updated_at < ?`,
        [cutoff]
      );
      stats.abandonedDraftCount = Number((_h = (_g = abandonedRows[0]) == null ? void 0 : _g.cnt) != null ? _h : 0);
      if (stats.abandonedDraftCount > 0) {
        const sampleRows = await safeQuery(
          `SELECT id, updated_at FROM ${quoted}
           WHERE published_at IS NULL AND updated_at < ?
           ORDER BY updated_at ASC LIMIT ${SAMPLE_SIZE}`,
          [cutoff]
        );
        stats.sampleAbandonedDraftIds = sampleRows.map((row) => row.id);
        stats.oldestDraftAt = (_j = (_i = sampleRows[0]) == null ? void 0 : _i.updated_at) != null ? _j : void 0;
      }
      const staleCutoff = thresholdDate(now, thresholds.staleContentDays);
      const staleRows = await safeQuery(
        `SELECT COUNT(*) AS cnt FROM ${quoted}
         WHERE published_at IS NOT NULL AND updated_at < ?`,
        [staleCutoff]
      );
      stats.staleContentCount = Number((_l = (_k = staleRows[0]) == null ? void 0 : _k.cnt) != null ? _l : 0);
      if (stats.staleContentCount > 0) {
        const staleSample = await safeQuery(
          `SELECT id, updated_at FROM ${quoted}
           WHERE published_at IS NOT NULL AND updated_at < ?
           ORDER BY updated_at ASC LIMIT ${SAMPLE_SIZE}`,
          [staleCutoff]
        );
        stats.sampleStaleIds = staleSample.map((row) => row.id);
        stats.oldestStaleUpdatedAt = (_n = (_m = staleSample[0]) == null ? void 0 : _m.updated_at) != null ? _n : void 0;
      }
    }
  }
  if (hasWorkflow && await columnExists(table, "workflow_status") && hasUpdatedAt) {
    const cutoff = thresholdDate(now, thresholds.workflowStuckDays);
    const stuckRows = await safeQuery(
      `SELECT COUNT(*) AS cnt FROM ${quoted}
       WHERE workflow_status = 'review' AND updated_at < ?`,
      [cutoff]
    );
    stats.workflowStuckCount = Number((_p = (_o = stuckRows[0]) == null ? void 0 : _o.cnt) != null ? _p : 0);
    if (stats.workflowStuckCount > 0) {
      const stuckSample = await safeQuery(
        `SELECT id FROM ${quoted}
         WHERE workflow_status = 'review' AND updated_at < ?
         ORDER BY updated_at ASC LIMIT ${SAMPLE_SIZE}`,
        [cutoff]
      );
      stats.sampleWorkflowStuckIds = stuckSample.map((row) => row.id);
    }
  }
  if (((_r = (_q = schema.options) == null ? void 0 : _q.i18n) == null ? void 0 : _r.enabled) === true && await columnExists(table, "document_id")) {
    const groupRows = await safeQuery(
      `SELECT document_id, COUNT(*) AS cnt FROM ${quoted}
       GROUP BY document_id
       ORDER BY document_id ASC`,
      []
    );
    const singleLocaleDocs = groupRows.filter((row) => Number(row.cnt) === 1);
    stats.missingTranslationsCount = singleLocaleDocs.length;
    if (singleLocaleDocs.length > 0) {
      const docIds = singleLocaleDocs.slice(0, SAMPLE_SIZE).map((row) => row.document_id);
      if (docIds.length > 0) {
        const placeholders = docIds.map(() => "?").join(", ");
        const sampleRows = await safeQuery(
          `SELECT id FROM ${quoted}
           WHERE document_id IN (${placeholders})
           ORDER BY id ASC LIMIT ${SAMPLE_SIZE}`,
          docIds
        );
        stats.sampleMissingTranslationsIds = sampleRows.map((row) => row.id);
      }
    }
  }
  return stats;
}
async function collectMediaStats(schemas, thresholds, now) {
  var _a, _b, _c, _d, _e, _f;
  const stats = {
    totalCount: 0,
    orphanCount: 0,
    missingAltCount: 0,
    heavyAssetCount: 0
  };
  const mediaSchema = schemas.find((s) => s.info.singularName === "media");
  if (!mediaSchema) {
    return stats;
  }
  const table = tableNameOf(mediaSchema);
  if (!await tableExists(table)) {
    return stats;
  }
  const quoted = quoteIdent(table);
  const totalRows = await safeQuery(`SELECT COUNT(*) AS cnt FROM ${quoted}`, []);
  stats.totalCount = Number((_b = (_a = totalRows[0]) == null ? void 0 : _a.cnt) != null ? _b : 0);
  const mimeColumn = await columnExists(table, "mimeType") ? "mimeType" : await columnExists(table, "mime_type") ? "mime_type" : null;
  if (await columnExists(table, "alt") && mimeColumn) {
    const mimeQuoted = quoteIdent(mimeColumn);
    const missingAltRows = await safeQuery(
      `SELECT COUNT(*) AS cnt FROM ${quoted}
       WHERE (alt IS NULL OR alt = '') AND ${mimeQuoted} LIKE 'image/%'`,
      []
    );
    stats.missingAltCount = Number((_d = (_c = missingAltRows[0]) == null ? void 0 : _c.cnt) != null ? _d : 0);
    if (stats.missingAltCount > 0) {
      const sample = await safeQuery(
        `SELECT id FROM ${quoted}
         WHERE (alt IS NULL OR alt = '') AND ${mimeQuoted} LIKE 'image/%'
         ORDER BY id ASC LIMIT ${SAMPLE_SIZE}`,
        []
      );
      stats.sampleMissingAltIds = sample.map((row) => row.id);
    }
  }
  if (await columnExists(table, "size")) {
    const heavyRows = await safeQuery(
      `SELECT COUNT(*) AS cnt FROM ${quoted} WHERE size > ?`,
      [HEAVY_ASSET_BYTES]
    );
    stats.heavyAssetCount = Number((_f = (_e = heavyRows[0]) == null ? void 0 : _e.cnt) != null ? _f : 0);
    if (stats.heavyAssetCount > 0) {
      const sample = await safeQuery(
        `SELECT id FROM ${quoted} WHERE size > ?
         ORDER BY size DESC LIMIT ${SAMPLE_SIZE}`,
        [HEAVY_ASSET_BYTES]
      );
      stats.sampleHeavyAssetIds = sample.map((row) => row.id);
    }
  }
  void thresholds.orphanMediaDays;
  return stats;
}

export { automationAdvisor_post as default };
//# sourceMappingURL=automation-advisor.post.mjs.map
