function generatePgSchemaSql(context) {
  const { spec, models } = context;
  const allModels = [];
  if (Array.isArray(models) && models.length > 0) {
    for (const m of models) if (m?.name) allModels.push([m.name, m]);
  } else if (spec?.components) {
    const components = Array.isArray(spec.components) ? spec.components : Object.values(spec.components);
    for (const comp of components) {
      const ms = comp?.models;
      if (!ms) continue;
      const entries = Array.isArray(ms) ? ms.map((m) => [m.name, m]) : Object.entries(ms);
      for (const [name, m] of entries) {
        if (name) allModels.push([name, m]);
      }
    }
  }
  const banner = `-- ============================================================
-- ${spec?.metadata?.component || "Application"} \u2014 schema.sql
-- Generated by @specverse/engines (postgres-native).
-- Run via: psql "$POSTGRES_URL" -f src/db/schema.sql
-- Re-runnable: every statement is IF NOT EXISTS / idempotent.
-- ============================================================
CREATE EXTENSION IF NOT EXISTS "pgcrypto";  -- for gen_random_uuid()
`;
  if (allModels.length === 0) {
    return banner + "\n-- No models declared. (Add one to your spec to populate this file.)\n";
  }
  const tableBlocks = allModels.map(([name, model]) => generateTable(name, model));
  return banner + "\n" + tableBlocks.join("\n\n") + "\n";
}
function generateTable(modelName, model) {
  const table = model?.storage?.table || modelName.toLowerCase() + "s";
  const attrs = model.attributes;
  if (!attrs) {
    return `-- ${modelName}
CREATE TABLE IF NOT EXISTS "${table}" (
  id TEXT PRIMARY KEY
);`;
  }
  const list = Array.isArray(attrs) ? attrs.map((a) => [a.name, a]) : Object.entries(attrs);
  const compositePk = parseColumnList(model.keys ?? model.primaryKey);
  const compositeUniques = parseCompositeUnique(model.unique);
  const columns = [];
  const indexes = [];
  let hasIdColumn = false;
  const compositePkSet = new Set(compositePk);
  for (const [colName, attr] of list) {
    if (!colName) continue;
    const partOfCompositePk = compositePkSet.has(colName);
    const { sql, isPk, isUnique } = columnDef(colName, attr, modelName, partOfCompositePk);
    if (isPk) hasIdColumn = true;
    columns.push("  " + sql);
    if (isUnique && !isPk && !partOfCompositePk) {
      indexes.push(
        `CREATE UNIQUE INDEX IF NOT EXISTS "${table}_${colName}_uq" ON "${table}" ("${colName}");`
      );
    } else if (colName.endsWith("Id") && colName !== "id" && !partOfCompositePk) {
      indexes.push(
        `CREATE INDEX IF NOT EXISTS "${table}_${colName}_idx" ON "${table}" ("${colName}");`
      );
    }
    const partial = parsePartialIndex(attr.index);
    if (partial) {
      const indexName = `${table}_${colName}${partial.unique ? "_uq" : "_idx"}_partial`;
      const uniqueClause = partial.unique ? "UNIQUE " : "";
      const whereClause = partial.where ? ` WHERE ${partial.where}` : "";
      indexes.push(
        `CREATE ${uniqueClause}INDEX IF NOT EXISTS "${indexName}" ON "${table}" ("${colName}")${whereClause};`
      );
    }
  }
  if (compositePk.length === 0 && !hasIdColumn) {
    columns.unshift('  "id" TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text');
  }
  const declaredNames = new Set(list.map(([n]) => n));
  if (!declaredNames.has("createdAt")) {
    columns.push('  "createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()');
  }
  if (!declaredNames.has("updatedAt")) {
    columns.push('  "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()');
  }
  const lifecycles = Array.isArray(model.lifecycles) ? model.lifecycles : model.lifecycles ? Object.entries(model.lifecycles).map(([name, lc]) => ({ name, ...lc })) : [];
  for (const lc of lifecycles) {
    const lcName = lc?.name;
    if (!lcName || declaredNames.has(lcName)) continue;
    const initial = deriveInitialState(lc);
    const defaultClause = initial ? ` DEFAULT '${initial.replace(/'/g, "''")}'` : "";
    columns.push(`  "${lcName}" TEXT NOT NULL${defaultClause}`);
  }
  if (compositePk.length > 0) {
    columns.push(`  PRIMARY KEY (${compositePk.map(quoteIdent).join(", ")})`);
  }
  for (const cols of compositeUniques) {
    columns.push(`  UNIQUE (${cols.map(quoteIdent).join(", ")})`);
  }
  const indexBlock = indexes.length > 0 ? "\n\n" + indexes.join("\n") : "";
  return `-- ${modelName}
CREATE TABLE IF NOT EXISTS "${table}" (
${columns.join(",\n")}
);${indexBlock}`;
}
function columnDef(name, attr, _modelName, forceNotNull) {
  const required = !!attr.required || forceNotNull;
  const unique = !!attr.unique;
  const type = (attr.type || "String").toLowerCase();
  const isIdColumn = name === "id";
  if (isIdColumn && !forceNotNull) {
    const notGenerated = ["none", "provided", "external"].includes(
      String(attr.auto ?? "").toLowerCase()
    );
    if (notGenerated) {
      return { sql: `"id" ${type === "uuid" ? "UUID" : "TEXT"} PRIMARY KEY`, isPk: true, isUnique: true };
    }
    if (type === "uuid") {
      return { sql: `"id" UUID PRIMARY KEY DEFAULT gen_random_uuid()`, isPk: true, isUnique: true };
    }
    return { sql: `"id" TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text`, isPk: true, isUnique: true };
  }
  let sqlType;
  switch (type) {
    case "integer":
    case "int":
      sqlType = "INTEGER";
      break;
    case "bigint":
      sqlType = "BIGINT";
      break;
    case "float":
    case "number":
    case "double":
      sqlType = "DOUBLE PRECISION";
      break;
    case "boolean":
    case "bool":
      sqlType = "BOOLEAN";
      break;
    case "datetime":
    case "date":
    case "timestamp":
      sqlType = "TIMESTAMPTZ";
      break;
    case "uuid":
      sqlType = "UUID";
      break;
    case "json":
    case "jsonb":
      sqlType = "JSONB";
      break;
    default:
      sqlType = "TEXT";
  }
  const nullness = required ? " NOT NULL" : "";
  const uniqueClause = unique ? " UNIQUE" : "";
  const defaultClause = formatColumnDefault(attr.default, type);
  return {
    sql: `"${name}" ${sqlType}${nullness}${uniqueClause}${defaultClause}`,
    isPk: false,
    isUnique: unique
  };
}
function deriveInitialState(lc) {
  if (!lc) return null;
  if (typeof lc.initial === "string") return lc.initial;
  if (Array.isArray(lc.states) && lc.states.length > 0) {
    const first = lc.states[0];
    return typeof first === "string" ? first : first?.name ?? null;
  }
  if (typeof lc.flow === "string") {
    const head = lc.flow.split(/\s*->\s*/)[0]?.trim();
    return head || null;
  }
  if (lc.transitions && typeof lc.transitions === "object") {
    const first = Object.values(lc.transitions)[0];
    if (typeof first === "string") return first.split(/\s*->\s*/)[0]?.trim() || null;
  }
  return null;
}
function formatColumnDefault(value, type) {
  if (value === void 0 || value === null) return "";
  if (typeof value === "boolean") return ` DEFAULT ${value}`;
  if (typeof value === "number") return ` DEFAULT ${value}`;
  if (typeof value === "string") {
    if (/^-?\d+(\.\d+)?$/.test(value)) return ` DEFAULT ${value}`;
    if (value === "true" || value === "false") return ` DEFAULT ${value}`;
    if (value === "now" && (type === "datetime" || type === "date" || type === "timestamp")) {
      return ` DEFAULT NOW()`;
    }
    return ` DEFAULT '${value.replace(/'/g, "''")}'`;
  }
  return "";
}
function parseColumnList(raw) {
  if (raw === void 0 || raw === null) return [];
  if (typeof raw === "string") return [raw];
  if (!Array.isArray(raw)) return [];
  if (raw.length === 0) return [];
  if (typeof raw[0] === "string") return raw.filter((x) => typeof x === "string");
  if (Array.isArray(raw[0])) {
    return raw[0].filter((x) => typeof x === "string");
  }
  return [];
}
function parseCompositeUnique(raw) {
  if (raw === void 0 || raw === null) return [];
  if (!Array.isArray(raw) || raw.length === 0) return [];
  if (typeof raw[0] === "string") {
    return [raw.filter((x) => typeof x === "string")];
  }
  if (Array.isArray(raw[0])) {
    return raw.map((row) => row.filter((x) => typeof x === "string")).filter((row) => row.length > 0);
  }
  return [];
}
function parsePartialIndex(raw) {
  if (raw === void 0 || raw === null || raw === false) return null;
  if (raw === true) return { where: null, unique: false };
  if (typeof raw === "object") {
    const where = typeof raw.where === "string" && raw.where.trim() !== "" ? raw.where.trim() : null;
    const unique = !!raw.unique;
    return { where, unique };
  }
  return null;
}
function quoteIdent(name) {
  return '"' + name.replace(/"/g, '""') + '"';
}
export {
  generatePgSchemaSql as default
};
