/**
 * Builds schema-analysis.md from mongo-analysis-raw.json (Phase 1 report).
 */
import fs from "fs";
import path from "path";

const RAW = path.join(process.cwd(), "scripts", "mongo-analysis-raw.json");
const OUT = path.join(process.cwd(), "scripts", "schema-analysis.md");

const REF_HINT =
  /(Id$|userId|ownerId|customerId|propertyId|bookingId|cityId|walletId|couponId|actorId|authorId|partyUserId|refundId|withdrawalId|updatedById|reviewedBy|createdBy)/i;

type FieldSer = {
  types?: string[];
  nullishCount?: number;
  isArray?: boolean;
  nested?: Record<string, FieldSer>;
  sampleValues?: unknown[];
};

type ColSer = {
  count: number;
  indexes?: Array<{ name: string; key: Record<string, unknown>; unique?: boolean; sparse?: boolean }>;
  fields?: Record<string, FieldSer>;
  samples?: Record<string, unknown>[];
};

function describeField(name: string, f: FieldSer, indent = 0): string[] {
  const pad = "  ".repeat(indent);
  const types = (f.types || []).join("|") || "unknown";
  let line = `${pad}- \`${name}\`: ${types}`;
  if (f.isArray) line += " (array)";
  if (f.nullishCount) line += ` [nullish in scan: ${f.nullishCount}]`;
  const lines = [line];
  if (f.nested) {
    for (const [k, v] of Object.entries(f.nested)) {
      lines.push(...describeField(k === "__element__" ? "[element]" : k, v, indent + 1));
    }
  }
  return lines;
}

function classify(fields: Record<string, FieldSer> | undefined) {
  const nested: string[] = [];
  const arrays: string[] = [];
  const refs: string[] = [];
  for (const [k, f] of Object.entries(fields || {})) {
    if (f.isArray) {
      const el = f.nested?.__element__;
      if (el?.types?.includes("object")) arrays.push(`\`${k}\` — array of objects`);
      else arrays.push(`\`${k}\` — array (${(el?.types || ["unknown"]).join("|")})`);
    }
    if (f.types?.includes("object") && f.nested && !f.isArray) nested.push(`\`${k}\``);
    if (REF_HINT.test(k) && k !== "_id") refs.push(`\`${k}\` → string id (ref-like)`);
  }
  return { nested, arrays, refs };
}

function main() {
  const r = JSON.parse(fs.readFileSync(RAW, "utf8")) as {
    database: string;
    analyzedAt: string;
    collectionCount: number;
    collections: Record<string, ColSer>;
  };

  const md: string[] = [];
  md.push("# MongoDB Schema Analysis — سفر ليبيا (Safar Libya)");
  md.push("");
  md.push("> **Phase 1 only** — read-only analysis. MongoDB was **not** modified.");
  md.push("");
  md.push("## Connection & environment");
  md.push("");
  md.push("| Item | Value |");
  md.push("|---|---|");
  md.push(`| Database name | \`${r.database}\` |`);
  md.push(`| Analyzed at (UTC) | ${r.analyzedAt} |`);
  md.push(`| Collection count | ${r.collectionCount} |`);
  md.push("| Backend | Node.js / Express / Mongoose |");
  md.push("| Connection config | `MONGODB_URI` in `backend/.env` (credentials not printed) |");
  md.push("| ID style | String IDs (cuid-like), **not** BSON ObjectId — often ~25 chars |");
  md.push("");
  md.push("## Collections overview");
  md.push("");
  md.push("| # | Collection | Documents |");
  md.push("|---:|---|---:|");

  const names = Object.keys(r.collections).sort();
  let total = 0;
  names.forEach((name, idx) => {
    const c = r.collections[name].count;
    total += c;
    md.push(`| ${idx + 1} | \`${name}\` | ${c} |`);
  });
  md.push(`| | **Total documents** | **${total}** |`);
  md.push("");

  for (const name of names) {
    const col = r.collections[name];
    const { nested, arrays, refs } = classify(col.fields);

    md.push("---");
    md.push("");
    md.push(`## Collection: \`${name}\``);
    md.push("");
    md.push(`- **Document count:** ${col.count}`);
    md.push("- **Indexes:**");
    for (const ix of col.indexes || []) {
      md.push(
        `  - \`${ix.name}\`: ${JSON.stringify(ix.key)}${ix.unique ? " UNIQUE" : ""}${ix.sparse ? " sparse" : ""}`,
      );
    }
    md.push("");
    md.push("### Fields & types");
    md.push("");
    if (!col.fields || !Object.keys(col.fields).length) {
      if (name === "wallettopups") {
        md.push("_Empty collection (0 docs). Expected Mongoose shape (`WalletTopUp`):_");
        md.push("");
        md.push("- `_id`: string");
        md.push("- `userId`: string (ref User)");
        md.push("- `amountLyd`: number");
        md.push("- `bankName`: string");
        md.push("- `reference`: string");
        md.push("- `status`: string enum TOPUP_STATUSES (default PENDING)");
        md.push("- `reviewedBy`: string? (ref User)");
        md.push("- `reviewedAt`: date?");
        md.push("- `reviewNote`: string?");
        md.push("- `createdAt` / `updatedAt`: date (timestamps)");
      } else {
        md.push("_Empty collection — no documents to infer schema._");
      }
    } else {
      for (const [fname, finfo] of Object.entries(col.fields)) {
        md.push(...describeField(fname, finfo));
      }
    }
    md.push("");
    md.push("### Nested objects");
    md.push(nested.length ? nested.map((x) => `- ${x}`).join("\n") : "_None detected in scanned documents._");
    md.push("");
    md.push("### Arrays");
    md.push(arrays.length ? arrays.map((x) => `- ${x}`).join("\n") : "_None detected in scanned documents._");
    md.push("");
    md.push("### Reference-like fields (string IDs → other collections)");
    md.push(refs.length ? refs.map((x) => `- ${x}`).join("\n") : "_None._");
    md.push("");
    md.push("### Sample documents (redacted, up to 3)");
    md.push("");
    if (!(col.samples || []).length) {
      md.push("_No samples (empty collection)._");
    } else {
      (col.samples || []).forEach((s, idx) => {
        md.push("<details>");
        md.push(`<summary>Sample ${idx + 1} — _id=${String(s._id ?? "?")}</summary>`);
        md.push("");
        md.push("```json");
        md.push(JSON.stringify(s, null, 2));
        md.push("```");
        md.push("");
        md.push("</details>");
        md.push("");
      });
    }
    md.push("");
  }

  md.push("---");
  md.push("");
  md.push("## Notes for Phase 2 (design only — awaiting your approval)");
  md.push("");
  md.push(
    "1. **IDs:** Values are string cuid-like IDs (often longer than 24 chars). `CHAR(24)` will truncate — recommend `VARCHAR(32)` PK storing the original `_id`, plus optional `mongo_id` mirror for traceability.",
  );
  md.push(
    "2. **Heavy nesting:** `bookings.payment`, `bookings.invoice`; `properties.images[]`, `videos[]`, `blockedDates[]`; free-form `meta` / `appsettings.value`.",
  );
  md.push("3. **`cities.aliases`:** array of strings.");
  md.push("4. **Empty:** `wallettopups` (0 docs) — still map from Mongoose model in Phase 2.");
  md.push("5. MongoDB stays untouched as backup until you explicitly approve cutover.");
  md.push("");
  md.push("## Approval gate");
  md.push("");
  md.push("**Stop here.** Please review and approve before Phase 2 (`schema.sql`).");
  md.push("");

  fs.writeFileSync(OUT, md.join("\n"), "utf8");
  console.log(`Wrote ${OUT} (${fs.statSync(OUT).size} bytes)`);
}

main();
