"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ProjectService = void 0;
const constants = require("../constants");
const path = require("path");
const simple_git_1 = require("simple-git");
const decorators_1 = require("../common/decorators");
const constants_1 = require("../constants");
const decorators_2 = require("../common/decorators");
const yok_1 = require("../common/yok");
class ProjectService {
    constructor($options, $hooksService, $packageManager, $errors, $fs, $logger, $pacoteService, $projectDataService, $projectConfigService, $projectHelper, $projectNameService, $projectTemplatesService, $tempService, $staticConfig, $childProcess) {
        this.$options = $options;
        this.$hooksService = $hooksService;
        this.$packageManager = $packageManager;
        this.$errors = $errors;
        this.$fs = $fs;
        this.$logger = $logger;
        this.$pacoteService = $pacoteService;
        this.$projectDataService = $projectDataService;
        this.$projectConfigService = $projectConfigService;
        this.$projectHelper = $projectHelper;
        this.$projectNameService = $projectNameService;
        this.$projectTemplatesService = $projectTemplatesService;
        this.$tempService = $tempService;
        this.$staticConfig = $staticConfig;
        this.$childProcess = $childProcess;
    }
    async validateProjectName(opts) {
        let projectName = opts.projectName;
        if (!projectName) {
            this.$errors.failWithHelp("You must specify <App name> when creating a new project.");
        }
        projectName = await this.$projectNameService.ensureValidName(projectName, {
            force: opts.force,
        });
        const projectDir = this.getValidProjectDir(opts.pathToProject, projectName);
        if (this.$fs.exists(projectDir) && !this.$fs.isEmptyDir(projectDir)) {
            this.$errors.fail("Path already exists and is not empty %s", projectDir);
        }
        return projectName;
    }
    async createProject(projectOptions) {
        const projectName = await this.validateProjectName({
            projectName: projectOptions.projectName,
            force: projectOptions.force,
            pathToProject: projectOptions.pathToProject,
        });
        const projectDir = this.getValidProjectDir(projectOptions.pathToProject, projectName);
        this.$fs.createDirectory(projectDir);
        const appId = projectOptions.appId ||
            this.$projectHelper.generateDefaultAppId(projectName, constants.DEFAULT_APP_IDENTIFIER_PREFIX);
        this.$logger.trace(`Creating a new NativeScript project with name ${projectName} and id ${appId} at location ${projectDir}`);
        const projectCreationData = await this.createProjectCore({
            template: projectOptions.template,
            projectDir,
            ignoreScripts: projectOptions.ignoreScripts,
            appId: appId,
            projectName,
        });
        if (this.$options.git) {
            try {
                if (!this.$options.force) {
                    const git = (0, simple_git_1.default)(projectDir);
                    if (await git.checkIsRepo()) {
                        throw new Error("Already part of a git repository.");
                    }
                }
                await this.$childProcess.exec(`git init ${projectDir}`);
                await this.$childProcess.exec(`git -C ${projectDir} add --all`);
                await this.$childProcess.exec(`git -C ${projectDir} commit --no-verify -m "init"`);
            }
            catch (err) {
                this.$logger.trace("Unable to initialize git repository. Error is: ", err);
            }
        }
        this.$logger.trace(`Project ${projectName} was successfully created.`);
        return projectCreationData;
    }
    isValidNativeScriptProject(pathToProject) {
        try {
            const projectData = this.$projectDataService.getProjectData(pathToProject);
            return (!!projectData &&
                !!projectData.projectDir &&
                !!(projectData.projectIdentifiers.ios &&
                    projectData.projectIdentifiers.android));
        }
        catch (e) {
            return false;
        }
    }
    getValidProjectDir(pathToProject, projectName) {
        const selectedPath = path.resolve(pathToProject || ".");
        const projectDir = path.join(selectedPath, projectName);
        return projectDir;
    }
    async createProjectCore(projectCreationSettings) {
        const { template, projectDir, appId, projectName, ignoreScripts, } = projectCreationSettings;
        try {
            const templateData = await this.$projectTemplatesService.prepareTemplate(template, projectDir);
            await this.extractTemplate(projectDir, templateData);
            this.alterPackageJsonData(projectCreationSettings);
            this.$projectConfigService.writeDefaultConfig(projectDir, appId);
            await this.ensureAppResourcesExist(projectDir);
            await this.$packageManager.install(projectDir, projectDir, {
                disableNpmInstall: false,
                frameworkPath: null,
                ignoreScripts,
            });
        }
        catch (err) {
            this.$fs.deleteDirectory(projectDir);
            throw err;
        }
        await this.$hooksService.executeAfterHooks(constants_1.Hooks.createProject, {
            hookArgs: projectCreationSettings,
        });
        return { projectName, projectDir };
    }
    async extractTemplate(projectDir, templateData) {
        this.$fs.ensureDirectoryExists(projectDir);
        const fullTemplateName = templateData.version
            ? `${templateData.templateName}@${templateData.version}`
            : templateData.templateName;
        await this.$pacoteService.extractPackage(fullTemplateName, projectDir);
    }
    async ensureAppResourcesExist(projectDir) {
        const projectData = this.$projectDataService.getProjectData(projectDir);
        const appResourcesDestinationPath = projectData.getAppResourcesDirectoryPath(projectDir);
        if (!this.$fs.exists(appResourcesDestinationPath)) {
            this.$logger.trace("Project does not have App_Resources - fetching from default template.");
            this.$fs.createDirectory(appResourcesDestinationPath);
            const tempDir = await this.$tempService.mkdirSync("ns-default-template");
            await this.$pacoteService.extractPackage(constants.RESERVED_TEMPLATE_NAMES["default"], tempDir);
            const templateProjectData = this.$projectDataService.getProjectData(tempDir);
            const templateAppResourcesDir = templateProjectData.getAppResourcesDirectoryPath(tempDir);
            this.$fs.copyFile(path.join(templateAppResourcesDir, "*"), appResourcesDestinationPath);
        }
    }
    alterPackageJsonData(projectCreationSettings) {
        const { projectDir, projectName } = projectCreationSettings;
        const projectFilePath = path.join(projectDir, this.$staticConfig.PROJECT_FILE_NAME);
        let packageJsonData = this.$fs.readJson(projectFilePath);
        Object.keys(packageJsonData).forEach((key) => {
            if (key.startsWith("_") ||
                constants.TemplatesV2PackageJsonKeysToRemove.includes(key)) {
                delete packageJsonData[key];
            }
        });
        const packageJsonSchema = {
            name: projectName,
            main: "",
            version: "1.0.0",
            private: true,
            dependencies: {},
            devDependencies: {},
        };
        packageJsonData = Object.assign(packageJsonSchema, packageJsonData);
        this.$fs.writeJson(projectFilePath, packageJsonData);
    }
}
exports.ProjectService = ProjectService;
__decorate([
    (0, decorators_1.exported)("projectService"),
    (0, decorators_2.performanceLog)()
], ProjectService.prototype, "createProject", null);
__decorate([
    (0, decorators_1.exported)("projectService")
], ProjectService.prototype, "isValidNativeScriptProject", null);
__decorate([
    (0, decorators_2.performanceLog)()
], ProjectService.prototype, "extractTemplate", null);
__decorate([
    (0, decorators_2.performanceLog)()
], ProjectService.prototype, "ensureAppResourcesExist", null);
__decorate([
    (0, decorators_2.performanceLog)()
], ProjectService.prototype, "alterPackageJsonData", null);
yok_1.injector.register("projectService", ProjectService);
