import path from 'path';
import * as fs from 'fs';
import * as readline from 'readline';
import { slugify } from '../utils';

const { execSync } = require('child_process');
const loadingSpinner = require('loading-spinner');

interface Question {
  text: string;
  optional: boolean;
  answerKey: string;
  hint?: string;
}

interface Answers {
  [key: string]: string;
}

const workingDir = path.resolve(process.cwd());
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

const ANSWERS: Answers = {};
const QUESTIONS: Array<Question> = [
  {
    text: 'Brand name',
    optional: false,
    answerKey: 'brandName',
    hint: 'eg. Project Zero'
  },
  {
    text: 'Project description',
    optional: true,
    answerKey: 'projectDescription'
  },
  {
    text: 'Commerce URL (SERVICE_BACKEND_URL)',
    optional: true,
    answerKey: 'commerceUrl',
    hint: 'Can be changed later'
  }
];

const getAnswers = () => {
  return new Promise<Answers>((resolve) => {
    const question = QUESTIONS[Object.keys(ANSWERS).length];

    if (!question) {
      rl.close();
      resolve(ANSWERS);
      return;
    }

    let questionText = question.text;

    if (question.hint) {
      questionText += ` (${question.hint})`;
    }

    if (question.optional) {
      questionText += ` (optional)`;
    }

    rl.question(`${questionText}: `, (answer: string) => {
      if (!question.optional && !answer.length) {
        console.log('\x1b[31m%s\x1b[0m', `* This field is required\n`);
        resolve(getAnswers());
      } else {
        ANSWERS[question.answerKey] = answer;
        resolve(getAnswers());
      }
    });
  });
};

const updateFileContents = (
  filePath: string,
  replacements: { [key: string]: string }
): void => {
  let content = fs.readFileSync(filePath, { encoding: 'utf8' });
  Object.entries(replacements).forEach(([key, value]) => {
    if (filePath.endsWith('.json')) {
      content = content.replace(
        new RegExp(`"${key}":\\s*".*?"`, 'g'),
        `"${key}": "${value}"`
      );
    } else {
      content = content.replace(
        new RegExp(`^${key}=.*$`, 'gm'),
        `${key}=${value}`
      );
    }
  });
  fs.writeFileSync(filePath, content, { encoding: 'utf8' });
};

function updatePluginsFile(projectDir: string): void {
  const pluginsFilePath = path.join(projectDir, 'src', 'plugins.js');

  const content = 'module.exports = [];';

  fs.writeFileSync(pluginsFilePath, content, { encoding: 'utf8' });
}

function createEnvironmentFile(projectDir: string) {
  fs.copyFileSync(`${projectDir}/.env.example`, `${projectDir}/.env`);
}

function checkGitignore(projectDir: string) {
  const gitignorePath = path.join(projectDir, '.gitignore');
  const npmignorePath = path.join(projectDir, '.npmignore');

  if (!fs.existsSync(gitignorePath)) {
    fs.renameSync(npmignorePath, gitignorePath);
  } else if (fs.existsSync(npmignorePath)) {
    fs.rmSync(npmignorePath);
  }
}

export default async (): Promise<void> => {
  const minNodeVersion = 18;
  const currentNodeVersion = parseInt(
    process.version.replace('v', '').split('.')[0]
  );

  if (currentNodeVersion < minNodeVersion) {
    console.log(
      '\x1b[31m%s\x1b[0m',
      `Node version must be ${minNodeVersion} or higher.\n`.concat(
        `Current version is ${currentNodeVersion}. Please update your Node version.`
      )
    );

    process.exit(1);
  }

  const answers = await getAnswers();
  const brandName =
    answers.brandName === '.' ? path.basename(workingDir) : answers.brandName;
  const projectDir = answers.brandName === '.' ? workingDir : path.resolve(workingDir, slugify(brandName));
  const relativeProjectDir = answers.brandName === '.' ? '.' : slugify(brandName);

  if (!fs.existsSync(projectDir)) {
    fs.mkdirSync(projectDir, { recursive: true });
  }

  const templatesDir = path.resolve(
    __dirname,
    '../../../projectzero/app-template'
  );

  fs.cpSync(templatesDir, projectDir, { recursive: true });

  updatePluginsFile(projectDir);
  createEnvironmentFile(projectDir);
  checkGitignore(projectDir);

  fs.chmodSync(path.join(projectDir, 'build.sh'), 0o711);

  if (answers.projectDescription) {
    updateFileContents(path.join(projectDir, 'akinon.json'), {
      description: answers.projectDescription
    });
  }

  if (answers.commerceUrl) {
    updateFileContents(path.join(projectDir, '.env'), {
      SERVICE_BACKEND_URL: answers.commerceUrl
    });
  }

  updateFileContents(path.join(projectDir, 'package.json'), {
    name: slugify(brandName)
  });


  console.log('\x1b[34m%s\x1b[0m', '\n🚀 Installing packages...\n');

  execSync(`cd ${relativeProjectDir} && yarn install`, { stdio: 'ignore' });

  loadingSpinner.stop();

  const successMessage = `
✨ ${brandName} project is ready at \x1b[4m${projectDir}\x1b[0m

Within the directory, the following commands are available:

  \x1b[35m$ yarn dev\x1b[0m
    \x1b[32mLaunches the development server.\x1b[0m

  \x1b[35m$ yarn build\x1b[0m
    \x1b[32mCompiles the app into static files for production.\x1b[0m

  \x1b[35m$ yarn start\x1b[0m
    \x1b[32mRuns the production server.\x1b[0m
`;

  const getStartedMessage = answers.brandName === '.'
    ? 'To get started, you can type:\n\n  \x1b[35m$ yarn dev\x1b[0m\n'
    : `To get started, you can type:\n\n  \x1b[35m$ cd ${relativeProjectDir}\x1b[0m\n  \x1b[35m$ yarn dev\x1b[0m\n`;

  console.log('\x1b[32m%s\x1b[0m', successMessage);
  console.log('\x1b[36m%s\x1b[0m', getStartedMessage);
  console.log('\x1b[33m%s\x1b[0m', 'Project setup is complete\n');
  console.log('\x1b[33m%s\x1b[0m', 'Project Zero - Akinon\n');
};
