const fs = require('fs');
const process = require('process');
const path = require('path');
const YAML = require('yamljs');
const {execSync} = require('child_process');

const {promises} = fs;
const docConfig = './data/docs.yml';
const layoutTemplateFile = '/themes/docsy/layouts/projectDoc/baseof.html';
const NEXT = 'next';

init();

async function init() {
  try {
    const targetPath = path.join(__dirname, layoutTemplateFile)
    const docsList = await loadYaml(docConfig)
    const {tpl, docsInfo} = await traverseDocsList(docsList)
    await generateLayoutTemplate(targetPath, tpl)
    handleDocsFiles(docsInfo)
  } catch (err) {
    console.log(err);
    process.exit(1)
  }

}

function readDirSync(path, docInfo, replaceMarkdownText) {
  const pa = fs.readdirSync(path);
  pa.forEach(function (ele) {
    const filePath = path + "/" + ele
    const info = fs.statSync(filePath);
    if (info.isDirectory()) {
      readDirSync(filePath, docInfo, replaceMarkdownText);
      return;
    }
    if (isImage(ele)) {
      const {docName, version} = docInfo;
      const imgName = `${docName}_${version}_${ele}`;
      fs.copyFile(filePath, './static/images/' + imgName, function (err) {
        if (err) {
          throw err
        }
      });
      return;
    }
    const reg = /\.md/gi;
    const shouldFormat = reg.test(filePath);
    if (shouldFormat) {
      readFile(filePath, docInfo, replaceMarkdownText);
    }
  });
}

function readFile(filePath, docInfo, replaceMarkdownText) {
  fs.readFile(filePath, function (err, data) {
    if (err) {
      throw err
    } else {
      let codeTxt = data.toString();
      codeTxt = replaceMarkdownText(codeTxt, docInfo, filePath)
      writeFile(filePath, codeTxt);
    }
  });
}

function isImage(file) {
  var reg = /(.*)\.(jpg|jpeg|png|bmp|gif|ico|pcx|tif|tiff|raw|tga|webp|svg)$/;
  return reg.test(file)
}

function replaceMarkdownText(codeTxt, docInfo, filePath) {
  if (!/^([\s]*)(---[\s\S]*---)/.test(codeTxt)) {
    const {repoUrl, commitId, docName, version} = docInfo;
    const prefix = repoUrl.replace('.git', '/tree') + `/${commitId}`;
    const depth = filePath.split('/docs')[1].match(/\//g).length - 2;

    let title = codeTxt.trim().split('\n')[0]
    title = title.match(/(?<=([ ])).*/g)[0];
    title = title.replace(/:/g, '：')

    codeTxt =
        `---
title: ${title}
type: projectDoc
layout: baseof
---\n` + codeTxt;
    codeTxt = codeTxt
        .replace(/(\[[\s\S]*?\])\(([\s\S]*?)\)/g, function (match, p1, p2) {
          if (p2 && p2.startsWith('http') || isImage(p2)) {
            return match
          }
          if (p2.startsWith('../')) {
            const parentDepth = p2.match(/\.\.\//g).length;
            if (parentDepth >= depth) {
              const url = p2.replace(/\.\.\//g, '')
              return `${p1}(${prefix}/${url})`
            }
          }
          const str = p2
              .toLowerCase()
              .replace(/\.md/g, '')

          if (str.startsWith('#')) {
            return `${p1}(${str})`
          }
          if (str.startsWith('./')) {
            return `${p1}(./../${str.slice(2)})`
          }
          return `${p1}(../${str})`
        })
        .replace(/<img(.*?)src="(.*?)"(.*?)>/g, function (match, p1, p2, p3) {
          if (p2 && p2.startsWith('http')) {
            return match
          }
          const imgName = `${docName}_${version}_` + p2.split('/').pop();
          return `<img${p1}src="/images/${imgName}"${p3}>`
        })
        .replace(/(\!\[[\s\S]*?\])\((.*?)\)/g, function (match, p1, p2,) {
          if (p2 && p2.startsWith('http')) {
            return match
          }
          const imgName = `${docName}_${version}_` + p2.split('/').pop();
          return `${p1}(/images/${imgName})`
        })
  }
  return codeTxt
}

function writeFile(filePath, codeTxt) {
  fs.writeFile(filePath, codeTxt, function (err) {
    if (err) {
      throw err
    }
  });
}


async function traverseDocsList(result) {
  let tpl = '';
  const docsInfo = []
  for (const data of result) {
    for (const item of data.list) {
      if (!item.docs) {
        continue;
      }
      for (const doc of item.docs) {
        const {name, repo, repoUrl, docs, description, icon} = item;
        if (!repoUrl) continue;
        let {version, commitId} = doc;
        version = version.toLowerCase();
        commitId = commitId || version;
        const docName = repo === 'skywalking' ? 'main' : repo;
        const localPath = `/content/docs/${docName}/${version}`;
        const menuFileName = `${docName.replace(/\-|\./g, '_')}${version.replace(/\-|v|\./g, '_')}`;

        tpl += `{{ if in .File.Path "${localPath.split('/content/')[1]}" }}
                  <div class="description-wrapper">
                    <h5>
                    <img width="26" height="26" src="/images/project/${icon}.svg">
                    ${name}
                    </h5>
                    <p>${description}</p>
                  </div>
                  {{ $currentVersion := lower .Site.Data.docSidebar.${menuFileName}.version }}
                  <div class="version-wrapper">Version: 
                  <select class="version-select">
                  {{range .Site.Data.docSidebar.${menuFileName}.repoDocs}}
                    {{$version := lower .version}}
                    {{$versionName := .versionName}}
                    <option {{ cond (eq $currentVersion $version) "selected" "" }} value="{{$version}}">
                    {{if $versionName}}
                      {{$versionName}}
                    {{else}}
                      {{$version}}
                    {{end}}
                    </option>
                  {{end}}
                  </select>
                  </div>
                  
                  {{ partial "sidebar-menu.html" .Site.Data.docSidebar.${menuFileName} }}
                  <div class="commit-id">Commit Id: {{.Site.Data.docSidebar.${menuFileName}.commitId}}</div>
                {{ end }}\n`;

        execSync(`"./doc.sh" ${repo} ${repoUrl} ${commitId} ${localPath} ${menuFileName}`);
        const sha = execSync(`git -C ./tmp/${repo} rev-parse HEAD`);
        if(version === NEXT && sha){
          commitId = sha.toString().replace('\n', '');
        }
        docsInfo.push({localPath, repoUrl, commitId, docName, version})

        await handleMenuFiles(`./data/docSidebar/${menuFileName}.yml`, {
          version,
          commitId,
          docs,
        }, `/docs/${docName}/${version}`)
      }

    }
  }
  return {tpl, docsInfo}
}

async function generateLayoutTemplate(targetPath, tpl) {
  let codeTxt = await promises.readFile(targetPath, 'utf8');
  codeTxt = codeTxt.toString()
  codeTxt = codeTxt.replace(/(td-sidebar">)([\s\S]*)(<\/div>[\s\S]*<main)/, function (match, p1, p2, p3) {
    return `${p1}\n${tpl}\n${p3}`
  })
  await promises.writeFile(targetPath, codeTxt, 'utf8');
}

function handleDocsFiles(docsInfo) {
  docsInfo.forEach((docInfo) => {
    const {localPath} = docInfo
    const root = path.join(__dirname, localPath);
    readDirSync(root, docInfo, replaceMarkdownText);
  })
}

async function handleMenuFiles(menuFilePath, docInfo, localPath) {
  const nativeObject = await loadYaml(menuFilePath);
  const {version, commitId, docs} = docInfo
  nativeObject.version = version;
  nativeObject.commitId = commitId.slice(0, 7);
  nativeObject.repoDocs = docs;

  handleMenuPath(nativeObject.catalog, localPath)
  const yamlString = YAML.stringify(nativeObject, 2);
  await promises.writeFile(menuFilePath, yamlString, 'utf8');
}

function handleMenuPath(list, localPath) {
  list.forEach(item => {
    const pagePath = item.path;
    if (pagePath) {
      item.path = pagePath.startsWith('http') ? pagePath : (localPath + pagePath).toLowerCase();
    }
    if (item.catalog) {
      handleMenuPath(item.catalog, localPath)
    }
  })
}

function loadYaml(filePath) {
  return new Promise((resolve) => {
    YAML.load(filePath, function (result) {
      resolve(result)
    });
  })
}
