import R from "ramda"

import fs from "fs"
import path from "path"

import {execSync} from "child_process"

import {BLOCKS_REPO} from "../constants"
import {evolveArray, sideEffect} from "./common"

export const checkHasChanges = R.pipe(
  () => execSync(`git ls-files -m`),
  R.toString,
  R.trim,
  R.split(`\n`),
  R.filter(R.identity),
  R.length,
  Boolean,
)

export const checkLocalRemoteRelations = R.pipe(
  R.juxt([
    R.pipe(
      (branch) => execSync(`git rev-parse ${branch}`),
      R.toString,
      R.trim,
    ),
    R.pipe(
      (branch) => execSync(`git rev-parse origin/${branch}`),
      R.toString,
      R.trim,
    ),
    R.pipe(
      (branch) => execSync(`git merge-base ${branch} origin/${branch}`),
      R.toString,
      R.trim,
    ),
  ]),
  R.cond([
    [
      R.pipe(
        R.juxt([R.nth(0), R.nth(1)]),
        R.apply(R.equals),
      ),
      R.always(`UP_TO_DATE`),
    ],
    [
      R.pipe(
        R.juxt([R.nth(0), R.nth(2)]),
        R.apply(R.equals),
      ),
      R.always(`NEED_TO_PULL`),
    ],
    [
      R.pipe(
        R.juxt([R.nth(1), R.nth(2)]),
        R.apply(R.equals),
      ),
      R.always(`NEED_TO_PUSH`),
    ],
    [R.T, R.always(`DIVERGED`)],
  ]),
)

export const fetchBranch = (branch) => execSync(`git fetch origin ${branch}:refs/remotes/origin/${branch}`)

export const checkoutToOrigin = (branch) => execSync(`git checkout -b ${branch} origin/${branch}`)

export const checkoutTo = R.tryCatch(
  R.pipe(
    sideEffect((branch) => execSync(`git checkout ${branch}`)),
    sideEffect(
      R.when(
        R.pipe(
          checkLocalRemoteRelations,
          R.equals(`NEED_TO_PULL`),
        ),
        () => execSync(`git pull`),
      ),
    ),
  ),
  R.cond([
    [
      (error, branch) =>
        R.contains(`error: pathspec '${branch}' did not match any file(s) known to git.`, error.toString()),
      R.pipe(
        R.nthArg(1),
        sideEffect(fetchBranch),
        sideEffect(checkoutToOrigin),
      ),
    ],
  ]),
)

export const getCurrentBranch = R.pipe(
  () => execSync(`git rev-parse --abbrev-ref HEAD`),
  R.toString,
  R.trim,
)

export const getBranchBase = R.pipe(
  R.split(`/`),
  R.dropLast(1),
  R.join(`/`),
)

export const getCurrentBrunchBase = R.pipe(
  getCurrentBranch,
  getBranchBase,
)

export const getBranchType = R.pipe(
  R.split(`/`),
  R.last,
)

export const getCurrentBrunchType = R.pipe(
  getCurrentBranch,
  getBranchType,
)

const IGNORE_BRANCHES = [`uikit/master`]

export const getOriginHeads = R.once(
  R.pipe(
    () => execSync(`git ls-remote --heads origin`),
    R.toString,
    R.trim,
    R.split(`\n`),
    R.map(R.trim),
  ),
)

export const getOriginBranchesHashes = R.once(
  R.pipe(
    getOriginHeads,
    R.map(
      R.juxt([
        R.replace(/^.*?refs\/heads\//, ``),
        R.pipe(
          R.replace(/refs\/heads\/.*$/, ``),
          R.trim,
        ),
      ]),
    ),
    R.fromPairs,
  ),
)

export const getRemoteBranches = R.once(
  R.pipe(
    getOriginBranchesHashes,
    R.keys,
    R.reject(R.flip(R.contains)(IGNORE_BRANCHES)),
  ),
)

export const getRemoteBranchesByPrefix = (prefix) =>
  R.pipe(
    getRemoteBranches,
    R.map(R.split(`/`)),
    R.filter(
      R.pipe(
        R.nth(0),
        R.equals(prefix),
      ),
    ),
    R.map(R.slice(1, -1)),
    R.map(R.join(`/`)),
    R.uniq,
    R.filter(R.length),
  )

export const getWireframeBlocksList = getRemoteBranchesByPrefix(`w`)

export const getDesignBlocksList = getRemoteBranchesByPrefix(`d`)

export const getUikitsList = getRemoteBranchesByPrefix(`uikit`)

export const checkBlockExists = (getBlocksList) => (namespace, name) =>
  R.pipe(
    getBlocksList,
    R.contains(`${namespace}/${name}`),
  )()

export const checkWireframeBlockExists = checkBlockExists(getWireframeBlocksList)

export const checkDesignBlockExists = checkBlockExists(getDesignBlocksList)

export const createWireframeBlockBranch = (namespace, name, editorVersion) => {
  checkoutTo(`w/default/master/prod`)
  execSync(`git checkout -b w/${namespace}/${name}/dev`)

  let config = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), `package.json`)).toString())
  config = R.set(R.lensPath([`name`]), `wireframe-${namespace}-${name}`, config)
  config = R.set(R.lensPath([`editorVersion`]), editorVersion, config)
  fs.writeFileSync(path.resolve(process.cwd(), `package.json`), JSON.stringify(config, null, 4))

  execSync(`npm i`)
  execSync(`git add -A && git commit -m "init w/${namespace}/${name}"`)
}

export const createDesignBlockBranch = (namespace, name, wireframe, editorVersion) => {
  const wireframeName = R.replace(`/`, `-`, wireframe)

  checkoutTo(`d/default/master/prod`)
  execSync(`git checkout -b d/${namespace}/${name}/dev`)

  let config = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), `package.json`)).toString())
  config = R.set(R.lensPath([`name`]), `design-${namespace}-${name}`, config)
  config = R.set(
    R.lensPath([`dependencies`]),
    {[`wireframe-${wireframeName}`]: `${BLOCKS_REPO}#w/${wireframe}/prod`},
    config,
  )
  config = R.set(R.lensPath([`editorVersion`]), editorVersion, config)
  fs.writeFileSync(path.resolve(process.cwd(), `package.json`), JSON.stringify(config, null, 4))

  let index = fs.readFileSync(path.resolve(process.cwd(), `src/index.js`)).toString()
  index = R.replace(
    `import Component from 'wireframe-default-master/src/component'`,
    `import Component from 'wireframe-${wireframeName}/src/component'`,
    index,
  )
  fs.writeFileSync(path.resolve(process.cwd(), `src/index.js`), index)

  let style = fs.readFileSync(path.resolve(process.cwd(), `src/style.css`)).toString()
  style = R.replace(
    `@import "wireframe-default-master/src/style.css";`,
    `@import "wireframe-${wireframeName}/src/style.css";`,
    style,
  )
  fs.writeFileSync(path.resolve(process.cwd(), `src/style.css`), style)

  execSync(`npm i`)
  execSync(`git add -A && git commit -m "init d/${namespace}/${name}"`)
}

export const getInitialCommitHash = R.pipe(
  getCurrentBranch,
  R.juxt([
    R.always(`git merge-base`),
    R.pipe(
      R.split(`/`),
      evolveArray([, R.always(`default`), R.always(`master`), R.always(`prod`)]),
      R.join(`/`),
    ),
    R.identity,
  ]),
  R.join(` `),
  execSync,
  R.toString,
  R.trim,
)
