/**
 * Flatten a JSON Schema node into a flat list of (name, type, required,
 * description) rows, ready to feed into the existing parameter table.
 *
 * Rules:
 *   - ``type: object``  → one row per property.
 *   - ``type: array``   → recurse into items; prefix each row with ``[].``
 *                         so the caller sees "[].username string" etc.
 *   - Nested objects    → dot-joined path: ``category.name``. We expand
 *                         one level; deeper graphs get a single summary
 *                         row ("category.* (object)") to keep the table
 *                         readable.
 *
 * This is a presentation helper — not a full form generator. It powers
 * the read-only fields table shown in the docs longread.
 */

type JsonSchemaNode = Record<string, unknown> & {
    type?: string;
    properties?: Record<string, JsonSchemaNode>;
    required?: string[];
    items?: JsonSchemaNode;
    enum?: unknown[];
    description?: string;
    format?: string;
};

export interface SchemaField {
    name: string;
    type: string;
    required: boolean;
    description?: string;
}

const MAX_DEPTH = 2;

function describeType(node: JsonSchemaNode): string {
    if (!node.type && node.properties) return 'object';
    const base = node.type || 'any';
    if (base === 'array') {
        const itemType = node.items ? describeType(node.items) : 'any';
        return `array<${itemType}>`;
    }
    if (Array.isArray(node.enum) && node.enum.length > 0) {
        return `${base} enum`;
    }
    if (node.format) return `${base} (${node.format})`;
    return base;
}

export function schemaToFields(
    schema: JsonSchemaNode | undefined,
    prefix = '',
    depth = 0,
): SchemaField[] {
    if (!schema || depth > MAX_DEPTH) return [];

    // Unwrap arrays: show the inner item's fields, prefixed with ``[]``.
    if (schema.type === 'array') {
        if (!schema.items) {
            return [{ name: prefix || '[]', type: 'array', required: false }];
        }
        const inner = schemaToFields(schema.items, prefix ? `${prefix}[]` : '[]', depth);
        if (inner.length === 0) {
            return [
                {
                    name: prefix || '[]',
                    type: describeType(schema),
                    required: false,
                    description: schema.description,
                },
            ];
        }
        return inner;
    }

    // Primitives and unknown — single row.
    if (schema.type !== 'object' && !schema.properties) {
        return [
            {
                name: prefix || '(body)',
                type: describeType(schema),
                required: false,
                description: schema.description,
            },
        ];
    }

    // Objects — one row per property, recurse for nested objects/arrays.
    const required = new Set(schema.required ?? []);
    const rows: SchemaField[] = [];
    const props = schema.properties ?? {};
    for (const [key, node] of Object.entries(props)) {
        const fullName = prefix ? `${prefix}.${key}` : key;
        const isRequired = required.has(key);

        const isNestedExpandable =
            (node.type === 'object' && node.properties) ||
            (node.type === 'array' && node.items);

        if (isNestedExpandable && depth < MAX_DEPTH) {
            // Leaf summary row for the parent itself (so the user sees
            // its description) + recurse for inner fields.
            rows.push({
                name: fullName,
                type: describeType(node),
                required: isRequired,
                description: node.description,
            });
            rows.push(...schemaToFields(node, fullName, depth + 1));
        } else {
            rows.push({
                name: fullName,
                type: describeType(node),
                required: isRequired,
                description: node.description,
            });
        }
    }
    return rows;
}
