import { execSync } from "child_process";
import fs from "fs";
import path from "path";
import {
  createStandardBoilerplateAPI,
  createStandardBoilerplateUI,
} from "./boilerplate/createStandardBoilerplate";
import { Project } from "./models/models";
import {
  convertToValidPackageName,
  getLoginPrompts,
  getProjectsList
} from "./prompts/prompts";
import { printAsciiArt, printMessage } from "./utils/printMessage.util";
import { serviceFactory } from "./config/service-factory";
import { systemApiKeyService } from "./service/ordino.systemApiKey.service";
import { projectService } from "./service/ordino.project.service";
import { initializeAuthInterceptors } from "./config/axios.config";
import { packageVersion } from "./config/package-version";

// Environment to use for this execution
const ENVIRONMENT = "staging";

export async function stagingMain(): Promise<void> {
  try {
    // Set the environment for the service factory
    serviceFactory.setEnvironment(ENVIRONMENT);
    
    displayWelcomeBanner(packageVersion);

    const loginCredentials = await getLoginPrompts();
    const username = loginCredentials.username;
    const password = loginCredentials.password;
    let authToken;
    if (username !== "" && password!== ""){
      try{
        var response = await systemApiKeyService.generateSystemApiKey({username, password});
        authToken = response.extraInfo;
        initializeAuthInterceptors(authToken);
      }catch(error: any){
        handleError(error);
      }
    }else {
      printMessage("Provided credentials invalid", "32", true);
      return;
    }

    let projects = await projectService.getAllProjects();
    var projectSelected = await getProjectsList(projects);
    var project = await projectService.getProjectDetails(projectSelected.project.id);
    const appPath = setupProjectDirectory(
      convertToValidPackageName(project.name)
    );
    await generateBoilerplate(
      authToken,
      appPath,
      project
    );

    finalizeSetup(
      appPath,
      convertToValidPackageName(project.name),
      project.repositoryURL,
      project.includeExampleApps
    );
  } catch (error: any) {
    handleError(error);
  }
}

function displayWelcomeBanner(env = "DEV"): void {
  const art = `
   ░▒▓██████▓▒░  ░▒▓███████▓▒░  ░▒▓███████▓▒░  ░▒▓█▓▒░ ░▒▓███▓▒░  ░▒▓█▓▒░ ░▒▓██████▓▒░  
  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓████▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 
  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 
  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ 
  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓██▓▒░  ░▒▓█▓▒░░▒▓█▓▒░ 
  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓█▓▒░ ░▒▓█▓▒░  ░▒▓█▓▒░  ░▒▓█▓▒░░▒▓█▓▒░ 
   ░▒▓██████▓▒░  ░▒▓█▓▒░░▒▓█▓▒░ ░▒▓███████▓▒░  ░▒▓█▓▒░ ░▒▓█▓▒░   ░▒▓▒░    ░▒▓██████▓▒░  
  `;
  printAsciiArt(art);
  printMessage(
    `\n Welcome to Ordino CLI (V${env}) \n`,
    "33",
    true,
    true
  );
}

function handleMissingCompanyID(): void {
  printMessage(
          "You need a company ID to proceed. Please contact Ordino support.",
    "31",
    true
  );
}

function setupProjectDirectory(projectName: string): string {
  const appPath = path.join(process.cwd(), projectName);

  if (fs.existsSync(appPath)) {
    printMessage(`Directory ${projectName} already exists`, "31", true);
    printMessage(`Using existing directory`, "33", true);
  } else {
    fs.mkdirSync(appPath);
    printMessage(`Directory ${projectName} created`, "32", true);
  }

  return appPath;
}

async function generateBoilerplate(
  authToken: string,
  appPath: string,
  project: Project,
): Promise<void> {
  const includeSampleTest = project.includeExampleApps;
  const needGitActionSetup = true;
  if (project.gui != undefined) {
    await createStandardBoilerplateUI(
      appPath,
      project,
      includeSampleTest,
      authToken,
      needGitActionSetup,
      ENVIRONMENT,
      project.platform
    );
  } 
  if (project.api != undefined) {
    await createStandardBoilerplateAPI(
      appPath,
      project,
      includeSampleTest,
      authToken,
      needGitActionSetup,
      ENVIRONMENT,
      project.platform
    );
  }
}

function finalizeSetup(
  appPath: string,
  projectName: string,
  gitUrl: string | null,
  includeSampleTest: boolean
): void {
  printMessage("Installing dependencies...", "32", true);
  execSync("npm run initialize", { cwd: appPath, stdio: "inherit" });

  if (includeSampleTest) {
    printMessage("Running sample tests...", "32", true);
    execSync("npm run oi:run:test", { cwd: appPath, stdio: "inherit" });
  }

  if (gitUrl) {
    try {
      printMessage("Initializing Git repository...", "32", true);

      execSync("git init", { cwd: appPath, stdio: "inherit" });

      execSync("git add .", { cwd: appPath, stdio: "inherit" });
      execSync('git commit -m "Initial commit"', {
        cwd: appPath,
        stdio: "inherit",
      });
      execSync("git branch -M main", {
        cwd: appPath,
        stdio: "inherit",
      });

      execSync(`git remote add origin ${gitUrl}`, {
        cwd: appPath,
        stdio: "inherit",
      });

      execSync("git push -u origin main -f", {
        cwd: appPath,
        stdio: "inherit",
      });
    } catch (error) {
      printMessage(`Git push failed`, "31", true);
    }
  }

  printMessage(
    `\nYour local app "${projectName}" has been successfully created! 🚀`,
    "32",
    true
  );
  printMessage("To get started:", "34");
  printMessage(`  cd ${projectName}`, "35", true);
  printMessage(`  npm run oi:run:test`, "35", true);
  printMessage(`  npm run oi:open:test`, "35", true);
  printMessage("Welcome to Ordino Local!", "32", true);
}

function handleError(error: any): void {
  printMessage(`Error: ${error.message || error}`, "31", true);
  process.exit(1);
}