import fs from 'node:fs';
import path from 'node:path';
import { z } from 'zod';
import { camelCase, pascalCase } from '../../lib/strings.js';
import { errorResult, jsonResult, toErrorMessage } from '../shared.js';
import { requireLaravel } from './requireLaravel.js';
const RELATION_TYPES = [
    'belongsTo',
    'hasOne',
    'hasMany',
    'belongsToMany',
    'hasManyThrough',
    'morphTo',
    'morphMany',
    'morphOne',
];
function findModelFile(root, model) {
    const candidates = [
        path.join(root, 'app', 'Models', `${model}.php`),
        path.join(root, 'app', `${model}.php`),
    ];
    return candidates.find((file) => fs.existsSync(file)) ?? null;
}
function relationMethodSource(methodName, relation, relatedModel) {
    const related = `\\App\\Models\\${relatedModel}::class`;
    return `
    public function ${methodName}()
    {
        return $this->${relation}(${related});
    }
`;
}
function insertMethodBeforeClosingBrace(source, method) {
    const trimmed = method.trimEnd() + '\n';
    if (source.includes(`function ${trimmed.match(/function\s+(\w+)/)?.[1] ?? ''}(`)) {
        return source;
    }
    const lastBrace = source.lastIndexOf('}');
    if (lastBrace === -1) {
        throw new Error('Could not find closing class brace in model file.');
    }
    return `${source.slice(0, lastBrace)}${trimmed}${source.slice(lastBrace)}`;
}
export function registerGenerateEloquentRelationTool(server) {
    server.registerTool('generate_eloquent_relation', {
        title: 'Generate Eloquent Relation',
        description: 'Add an Eloquent relationship method to a model (belongsTo, hasMany, etc.), optionally also adding the inverse relation on the related model.',
        inputSchema: {
            model: z.string().min(1).describe('Model class name, e.g. "Post".'),
            relation: z.enum(RELATION_TYPES).describe('Eloquent relation type.'),
            related: z.string().min(1).describe('Related model class name, e.g. "User".'),
            method: z.string().optional().describe('Method name on the source model. Defaults from relation type.'),
            inverse: z
                .boolean()
                .optional()
                .default(false)
                .describe('If true, also add a sensible inverse method on the related model.'),
        },
    }, async ({ model, relation, related, method, inverse }) => {
        try {
            const laravel = requireLaravel();
            const modelName = pascalCase(model);
            const relatedName = pascalCase(related);
            const modelFile = findModelFile(laravel.root, modelName);
            if (!modelFile) {
                return errorResult(`Model file not found for "${modelName}" under app/Models or app/.`);
            }
            const defaultMethod = relation === 'belongsTo' || relation === 'hasOne' || relation === 'morphTo' || relation === 'morphOne'
                ? camelCase(relatedName)
                : `${camelCase(relatedName)}s`;
            const methodName = method?.trim() || defaultMethod;
            const before = fs.readFileSync(modelFile, 'utf8');
            const next = insertMethodBeforeClosingBrace(before, relationMethodSource(methodName, relation, relatedName));
            fs.writeFileSync(modelFile, next);
            const updated = [modelFile];
            let inverseMethod = null;
            let inverseFile = null;
            if (inverse) {
                const inverseMap = {
                    belongsTo: 'hasMany',
                    hasMany: 'belongsTo',
                    hasOne: 'belongsTo',
                    belongsToMany: 'belongsToMany',
                    morphMany: 'morphTo',
                    morphOne: 'morphTo',
                };
                const inverseRelation = inverseMap[relation];
                if (inverseRelation) {
                    inverseFile = findModelFile(laravel.root, relatedName);
                    if (!inverseFile) {
                        return errorResult(`Added ${methodName}() on ${modelName}, but related model "${relatedName}" was not found for the inverse.`);
                    }
                    inverseMethod =
                        inverseRelation === 'belongsTo' || inverseRelation === 'hasOne' || inverseRelation === 'morphTo'
                            ? camelCase(modelName)
                            : `${camelCase(modelName)}s`;
                    const relatedSource = fs.readFileSync(inverseFile, 'utf8');
                    fs.writeFileSync(inverseFile, insertMethodBeforeClosingBrace(relatedSource, relationMethodSource(inverseMethod, inverseRelation, modelName)));
                    updated.push(inverseFile);
                }
            }
            return jsonResult({
                model: modelName,
                related: relatedName,
                relation,
                method: methodName,
                inverseMethod,
                files: updated,
            });
        }
        catch (error) {
            return errorResult(toErrorMessage(error));
        }
    });
}
//# sourceMappingURL=generateEloquentRelation.js.map