"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.main = void 0;
const core_1 = require("@angular-devkit/core");
const strings_1 = require("@angular-devkit/core/src/utils/strings");
const schematics_1 = require("@angular-devkit/schematics");
const utils_1 = require("../../utils");
const formatting_1 = require("../../utils/formatting");
const name_parser_1 = require("../../utils/name.parser");
const source_root_helpers_1 = require("../../utils/source-root.helpers");
const ts = require("ts-morph");
function main(options) {
    options = transform(options);
    return (tree, context) => {
        if (!(0, source_root_helpers_1.isInSvrRootDirectory)(tree, [])) {
            throw new schematics_1.SchematicsException('This command must run in server root dir.');
        }
        if (isControllerExists(tree, options)) {
            (0, source_root_helpers_1.mergeSourceRoot)(options)(tree, context);
            options.interceptorType = 'generated';
            const interceptorPath = `${options.path}/${options.name}.${options.type}.${options.language}`;
            if (!tree.get(interceptorPath)) {
                return (0, schematics_1.branchAndMerge)((0, schematics_1.chain)([
                    (0, schematics_1.mergeWith)(generate(options)),
                    addDeclarationToParentIndex(options),
                ]))(tree, context);
            }
            return (0, schematics_1.branchAndMerge)((0, schematics_1.chain)([appendInceptor(options)]))(tree, context);
        }
        options.interceptorType = 'default';
        return (0, schematics_1.branchAndMerge)((0, schematics_1.chain)([(0, source_root_helpers_1.mergeSourceRoot)(options), (0, schematics_1.mergeWith)(generate(options))]));
    };
}
exports.main = main;
function transform(options) {
    const target = Object.assign({}, options);
    if (!target.name) {
        throw new schematics_1.SchematicsException('Option (controller) is required.');
    }
    const location = new name_parser_1.NameParser().parse(target);
    target.name = (0, formatting_1.normalizeToKebabOrSnakeCase)(location.name);
    target.path = (0, formatting_1.normalizeToKebabOrSnakeCase)(location.path);
    target.language = target.language !== undefined ? target.language : 'ts';
    target.type = 'interceptor';
    target.path = !target.flat
        ? target.path
        : (0, core_1.join)('customize', target.path, target.name);
    return target;
}
function isControllerExists(tree, options) {
    const controllerPath = `src/generated/${options.name}/${options.name}.controller.${options.language}`;
    if (!tree.get(controllerPath)) {
        return false;
    }
    const tsProject = new ts.Project({
        manipulationSettings: {
            indentationText: ts.IndentationText.TwoSpaces,
        },
    });
    const sourceFile = tsProject.addSourceFileAtPath(controllerPath);
    const controllerClass = sourceFile.getClassOrThrow(`${(0, strings_1.classify)(options.name)}Controller`);
    if (options.method) {
        controllerClass.getInstanceMethodOrThrow(options.method);
    }
    return true;
}
function generate(options) {
    return (context) => (0, schematics_1.apply)((0, schematics_1.url)((0, core_1.join)('./files', options.language, options.interceptorType)), [
        options.spec ? (0, schematics_1.noop)() : (0, schematics_1.filter)((path) => !path.endsWith('.spec.ts')),
        (0, schematics_1.template)(Object.assign(Object.assign({}, core_1.strings), options)),
        (0, schematics_1.move)(options.path),
    ])(context);
}
function appendInceptor(options) {
    return (tree) => {
        const interceptorPath = `${options.path}/${options.name}.${options.type}.${options.language}`;
        if (!tree.get(interceptorPath)) {
            throw new schematics_1.SchematicsException(`${interceptorPath} is not exist.`);
        }
        const tsProject = new ts.Project({
            manipulationSettings: {
                indentationText: ts.IndentationText.TwoSpaces,
            },
        });
        const sourceFile = tsProject.addSourceFileAtPath(interceptorPath);
        const interceptorName = `${(0, strings_1.classify)(options.name)}${options.method ? (0, strings_1.classify)(options.method) : ''}Interceptor`;
        const interceptorClass = sourceFile.getClass(interceptorName);
        if (interceptorClass) {
            throw new schematics_1.SchematicsException(`Class ${interceptorName} is already exist in ${interceptorPath}.`);
        }
        sourceFile.addStatements(`
@UseInterceptorFor(${(0, strings_1.classify)(options.name)}Controller${options.method ? ", '" + options.method + "'" : ""})
export class ${interceptorName} implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    return next.handle().pipe(tap((val) => console.log('After...', val)));
  }
}`);
        tree.overwrite(interceptorPath, sourceFile.getFullText());
        return tree;
    };
}
function addDeclarationToParentIndex(options) {
    return (tree) => {
        const filePath = 'src/customize/index.ts';
        const fileRef = tree.get(filePath);
        if (!fileRef) {
            throw new schematics_1.SchematicsException('src/customize/index.ts is not exist.');
        }
        const content = tree.read(filePath).toString();
        const exportFrom = new utils_1.ExportFromDeclarator();
        options.module = ('/' + filePath);
        tree.overwrite(filePath, exportFrom.declare(content, options));
        return tree;
    };
}
