import R from "ramda"
import Rx from "rxjs"

import inquirer from "inquirer"
import autocomplete from "inquirer-autocomplete-prompt"

import chalk from "chalk"

import logo from "./logo"

import {fillColumns} from "./utils/terminal"
import {
  getCurrentBrunchType,
  getWireframeBlocksList,
  getUikitsList,
  checkDesignBlockExists,
  createDesignBlockBranch,
  checkWireframeBlockExists,
  createWireframeBlockBranch,
  checkHasChanges,
} from "./utils/branches"
import {runServer, publishBlock} from "./utils"

import {isAuthenticated, authenticate} from "./services/github"
import {pipeAsync} from "./utils/common"

inquirer.registerPrompt(`autocomplete`, autocomplete)

const prompts$ = new Rx.Subject()
const answers$ = new Rx.Subject()

const runBranch = async ({createQuestions, handler}) => {
  console.log(`${chalk.yellow(fillColumns(`-`))}`)
  const questions = createQuestions()
  let counter = 0
  const answers = {}
  while (questions.length > counter) {
    const question = questions[counter]
    const when = question.when || R.T
    if (when(answers)) {
      prompts$.next(question)
      await answers$
        .first()
        .do(({name, answer}) => {
          answers[name] = answer
        })
        .toPromise()
    }
    counter += 1
  }
  handler(answers)
}

const branches = {
  start: {
    createQuestions: () => [
      {
        name: `initial`,
        type: `list`,
        message: `What do you want to do?`,
        choices: R.filter(R.identity)([
          {name: `Run dev server in folder`, value: `RUN_SERVER`},
          {name: `Run dev server in folder (editor-version)`, value: `RUN_SERVER_EDITOR`},
          {name: `Create new block`, value: `CREATE_BLOCK`},
          isAuthenticated && getCurrentBrunchType() === `dev` && {name: `Publish block`, value: `PUBLISH_BLOCK`},
          !isAuthenticated &&
            getCurrentBrunchType() === `dev` && {name: `Authenticate Github`, value: `AUTHENTICATE_GITHUB`},
        ]),
      },
    ],
    handler: R.cond([
      [R.propEq(`initial`, `RUN_SERVER`), runServer],
      [R.propEq(`initial`, `RUN_SERVER_EDITOR`), R.partial(runServer, [{type: `editor-version`}])],
      [
        R.propEq(`initial`, `CREATE_BLOCK`),
        R.ifElse(
          checkHasChanges,
          () => {
            console.log(`Can't create new block. There are uncommitted changes.`)
            runBranch(branches.start)
          },
          () => {
            runBranch(branches.createBlock)
          },
        ),
      ],
      [R.propEq(`initial`, `PUBLISH_BLOCK`), pipeAsync(publishBlock, () => runBranch(branches.start))],
      [R.propEq(`initial`, `AUTHENTICATE_GITHUB`), () => runBranch(branches.authenticateGithub)],
    ]),
  },
  createBlock: {
    createQuestions: () => [
      {
        name: `type`,
        type: `list`,
        message: `Choose type`,
        choices: [{name: `Wireframe`, value: `WIREFRAME`}, {name: `Design`, value: `DESIGN`}],
      },
      {
        name: `namespace`,
        type: `autocomplete`,
        message: `Choose namespace`,
        source: R.converge(
          R.pipe(
            pipeAsync,
            R.call,
          ),
          [
            R.pipe(
              R.nthArg(0),
              R.prop(`type`),
              R.flip(R.prop)({
                WIREFRAME: R.pipe(
                  R.always(R.range(1, 20)),
                  R.map(
                    R.pipe(
                      R.toString,
                      R.concat(`series-`),
                    ),
                  ),
                ),
                DESIGN: getUikitsList,
              }),
            ),
            R.pipe(
              R.nthArg(1),
              R.defaultTo(``),
              R.startsWith,
              R.filter,
            ),
          ],
        ),
      },
      {
        name: `name`,
        message: `Type new block name`,
        validate: R.converge(R.call, [
          R.pipe(
            R.nthArg(1),
            R.prop(`type`),
            R.flip(R.prop)({
              WIREFRAME: R.ifElse(
                checkWireframeBlockExists,
                R.always(`Wireframe block with this name is already exists!`),
                R.T,
              ),
              DESIGN: R.ifElse(checkDesignBlockExists, R.always(`Design block with this name is already exists!`), R.T),
            }),
          ),
          R.pipe(
            R.nthArg(1),
            R.prop(`namespace`),
          ),
          R.nthArg(0),
        ]),
      },
      {
        name: `wireframe`,
        type: `autocomplete`,
        message: `Choose wireframe`,
        source: async (answersSoFar, input) =>
          R.pipe(
            getWireframeBlocksList,
            R.filter(
              R.pipe(
                R.split(`/`),
                R.map(R.startsWith(input || ``)),
                R.any(R.identity),
              ),
            ),
          )(),
        default: null,
        when: R.pathEq([`type`], `DESIGN`),
      },
    ],
    handler: R.pipe(
      R.cond([
        [
          R.pathEq([`type`], `WIREFRAME`),
          R.pipe(
            R.props([`namespace`, `name`]),
            R.apply(createWireframeBlockBranch),
          ),
        ],
        [
          R.pathEq([`type`], `DESIGN`),
          R.pipe(
            R.props([`namespace`, `name`, `wireframe`]),
            R.apply(createDesignBlockBranch),
          ),
        ],
      ]),
      () => runBranch(branches.start),
    ),
  },
  authenticateGithub: {
    createQuestions: () => [
      {
        name: `type`,
        type: `list`,
        message: `Choose your authentication type`,
        choices: [
          {name: `With username/password`, value: `BASIC`},
          {name: `With personal access token`, value: `TOKEN`},
        ],
      },
      {
        name: `username`,
        message: `Type your github username`,
        when: R.propEq(`type`, `BASIC`),
      },
      {
        name: `password`,
        type: `password`,
        message: `Type your github password (never stored)`,
        when: R.propEq(`type`, `BASIC`),
      },
      {
        name: `token`,
        type: `password`,
        message: `Type your github access token`,
        when: R.propEq(`type`, `TOKEN`),
      },
    ],
    handler: R.pipe(
      R.evolve({
        type: R.toLower,
      }),
      authenticate,
      () => runBranch(branches.start),
    ),
  },
}

export const start = async () => {
  console.log(chalk.yellow(fillColumns(`=`)))
  console.log(chalk.blue(fillColumns(`M`, logo)))
  inquirer.prompt(prompts$).ui.process.subscribe((answer) => answers$.next(answer))
  runBranch(branches.start)
}
