module Ribosome.App.Templates.Boot where

import Exon (exon)

import Ribosome.App.Data (
  Cachix (Cachix),
  CachixKey (CachixKey),
  CachixName (CachixName),
  Github (Github),
  GithubOrg (GithubOrg),
  GithubRepo (GithubRepo),
  ProjectName (ProjectName),
  cachixTek,
  )

cachixOpts :: Cachix -> Text
cachixOpts :: Cachix -> Text
cachixOpts (Cachix (CachixName Text
name) (CachixKey Text
key)) =
  [exon|
  \ '--option', 'extra-substituters', 'https://#{name}.cachix.org',
  \ '--option', 'extra-trusted-public-keys', '#{key}',|]

github :: ProjectName -> Github -> Text
github :: ProjectName -> Github -> Text
github (ProjectName Text
name) (Github (GithubOrg Text
org) (GithubRepo Text
repo)) =
  [exon|let s:gh_exe = s:repo . '/github-exe'
let s:fetch_cmd = [
  \ 'curl',
  \ '--no-progress-meter',
  \ '--location',
  \ '--create-dirs',
  \ '--output',
  \ s:gh_exe,
  \ 'https://github.com/#{org}/#{repo}/releases/download/latest/#{name}'
  \ ]
|]

build :: ProjectName -> Bool -> Text
build :: ProjectName -> Bool -> Text
build (ProjectName Text
name) = \case
  Bool
True ->
    [exon|if filereadable(s:exe)
  call s:run(s:exe)
elseif filereadable(s:gh_exe)
  call s:run(s:gh_exe)
else
  if executable('nix') && !get(g:, '#{name}_fetch_bin', 0)
    echo 'Building #{name}...'
    call s:nix_build()
  else
    echo 'Fetching the #{name} executable from github...'
    call s:fetch_bin()
  endif
endif
|]
  Bool
False ->
    [exon|if filereadable(s:exe)
  call s:run(s:exe)
else
  if executable('nix')
    echo 'Building #{name}...'
    call s:nix_build()
  else
    echo 'Cannot build #{name} without Nix'
  endif
endif
|]

vimBoot :: ProjectName -> Maybe Github -> Maybe Cachix -> Text
vimBoot :: ProjectName -> Maybe Github -> Maybe Cachix -> Text
vimBoot pn :: ProjectName
pn@(ProjectName Text
name) Maybe Github
gh Maybe Cachix
cachix =
  [exon|let s:repo = fnamemodify(expand('<sfile>'), ':p:h:h')
let s:exe = s:repo . '/result/bin/#{name}'
let s:build_cmd = [
  \ 'nix',#{cachixOpts (fromMaybe cachixTek cachix)}
  \ 'build', '.##{name}',
  \ ]
let s:errors = []

function! s:run(exe) abort "{{{
  call jobstart([a:exe] + get(g:, '#{name}_cli_args', []), { 'rpc': v:true, 'cwd': s:repo, })
endfunction "}}}

function! s:error(msg) abort "{{{
  call nvim_echo(
        \ [[a:msg . ":\n", 'Error']] +
        \ map(s:errors, { i, s -> [s, 'Error'] }),
        \ v:false, {})
endfunction "}}}

function! s:built(code) abort "{{{
  if a:code == 0
    call s:run(s:exe)
  else
    call s:error('Failed to build #{name}')
  endif
endfunction "}}}

function! s:nix_build() abort "{{{
  call jobstart(s:build_cmd, {
        \ 'cwd': s:repo,
        \ 'on_exit': { i, code, n -> s:built(code) },
        \ 'on_stderr': { i, data, n -> s:stderr(data) },
        \ })
endfunction "}}}

function! s:fetched(code) abort "{{{
  if a:code == 0
    call system(['chmod', '+x',  s:gh_exe])
    call s:run(s:gh_exe)
  else
    call s:error('Failed to fetch the #{name} executable from github')
  endif
endfunction "}}}

function! s:stderr(data) abort "{{{
  call extend(s:errors, a:data)
endfunction "}}}

function! s:fetch_bin() abort "{{{
  call jobstart(s:fetch_cmd, {
        \ 'cwd': s:repo,
        \ 'on_exit': { i, code, n -> s:fetched(code) },
        \ 'on_stderr': { i, data, n -> s:stderr(data) },
        \ })
endfunction "}}}

#{maybe "" (github pn) gh}
#{build pn (isJust gh)}
|]