"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;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
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;
    }
    validateProjectName(opts) {
        return __awaiter(this, void 0, void 0, function* () {
            let projectName = opts.projectName;
            if (!projectName) {
                this.$errors.failWithHelp("You must specify <App name> when creating a new project.");
            }
            projectName = yield 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;
        });
    }
    createProject(projectOptions) {
        return __awaiter(this, void 0, void 0, function* () {
            const projectName = yield 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 = yield this.createProjectCore({
                template: projectOptions.template,
                projectDir,
                ignoreScripts: projectOptions.ignoreScripts,
                appId: appId,
                projectName,
            });
            if (this.$options.git) {
                try {
                    if (!this.$options.force) {
                        const git = simple_git_1.default(projectDir);
                        if (yield git.checkIsRepo()) {
                            throw new Error("Already part of a git repository.");
                        }
                    }
                    yield this.$childProcess.exec(`git init ${projectDir}`);
                    yield this.$childProcess.exec(`git -C ${projectDir} add --all`);
                    yield 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;
    }
    createProjectCore(projectCreationSettings) {
        return __awaiter(this, void 0, void 0, function* () {
            const { template, projectDir, appId, projectName, ignoreScripts, } = projectCreationSettings;
            try {
                const templateData = yield this.$projectTemplatesService.prepareTemplate(template, projectDir);
                yield this.extractTemplate(projectDir, templateData);
                this.alterPackageJsonData(projectCreationSettings);
                this.$projectConfigService.writeDefaultConfig(projectDir, appId);
                yield this.ensureAppResourcesExist(projectDir);
                yield this.$packageManager.install(projectDir, projectDir, {
                    disableNpmInstall: false,
                    frameworkPath: null,
                    ignoreScripts,
                });
            }
            catch (err) {
                this.$fs.deleteDirectory(projectDir);
                throw err;
            }
            yield this.$hooksService.executeAfterHooks(constants_1.Hooks.createProject, {
                hookArgs: projectCreationSettings,
            });
            return { projectName, projectDir };
        });
    }
    extractTemplate(projectDir, templateData) {
        return __awaiter(this, void 0, void 0, function* () {
            this.$fs.ensureDirectoryExists(projectDir);
            const fullTemplateName = templateData.version
                ? `${templateData.templateName}@${templateData.version}`
                : templateData.templateName;
            yield this.$pacoteService.extractPackage(fullTemplateName, projectDir);
        });
    }
    ensureAppResourcesExist(projectDir) {
        return __awaiter(this, void 0, void 0, function* () {
            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 = yield this.$tempService.mkdirSync("ns-default-template");
                yield 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);
    }
}
__decorate([
    decorators_1.exported("projectService"),
    decorators_2.performanceLog()
], ProjectService.prototype, "createProject", null);
__decorate([
    decorators_1.exported("projectService")
], ProjectService.prototype, "isValidNativeScriptProject", null);
__decorate([
    decorators_2.performanceLog()
], ProjectService.prototype, "extractTemplate", null);
__decorate([
    decorators_2.performanceLog()
], ProjectService.prototype, "ensureAppResourcesExist", null);
__decorate([
    decorators_2.performanceLog()
], ProjectService.prototype, "alterPackageJsonData", null);
exports.ProjectService = ProjectService;
yok_1.injector.register("projectService", ProjectService);
