// node index

const fs = require('fs');
const path = require('path');
const moment = require('moment');
const template = require('art-template');
const { getSchema } = require('@mrleebo/prisma-ast');
const { stringify } = require('querystring');

let allStartTimeStamp = moment().valueOf();

Array.prototype.remove = function(val) { 
  var index = this.indexOf(val); 
  if (index > -1) { 
    this.splice(index, 1); 
  } 
};

function toFirstLowerCase(str) {
  return str.replace(str[0], str[0].toLocaleLowerCase());
}

// 首字母小写过滤器
template.defaults.imports.toFirstLowerCase = (str) =>{
  return toFirstLowerCase(str);
}

const replaceStr = (str, index, char) => {
  return str.substring(0, index) + char + str.substring(index + 1);
}

function toFirstUpperCase(str) { 
  // go run github.com/prisma/prisma-client-go generate 决定了如果第一个为I，则第二个为d的话，则d变为大写
  str = str.replace(str[0], str[0].toUpperCase());
  for (let i = 0; i < str.length - 1; i++) {
    if (str[i] === 'I' && str[i+1] === 'd') {
      str = replaceStr(str, i+1, 'D');
    }
  }
  
  return str;
}

// 首字母大写过滤器
template.defaults.imports.toFirstUpperCase = (str) =>{
  return toFirstUpperCase(str);
}

function isOne2Many(c, tableName, tableObj) {
  const { fieldType } = c;
  table = tableObj[fieldType];
  if (!table) {
    return false;
  }
  let flag = false;
  table.columns.forEach((e) => {
    if (e.fieldType === tableName && !e.array) {
      flag = true;
    }
  })
  return flag;
}

function isMany2One(c, table) {
  const { name } = c;
  let flag = false;
  table.columns.forEach((e) => {
    if (e.attributes !== undefined && e.attributes.length) {
      // console.log(table.name, name, JSON.stringify(e.attributes));
      if (e.attributes[0].args !== undefined && e.attributes[0].args.length) {
        e.attributes[0].args.forEach((ee) => {
          // console.log(JSON.stringify(ee));
          if (ee.value.value !== undefined && ee.value.value.args.includes(name)) {
            flag = true;
          }
        })
      }
    }
  })
  return flag;
}

function parseSwaggerFieldType(fieldType) {
  switch(fieldType) {
    case 'String': return 'string';
    case 'DateTime': return 'string';
    case 'Int': return 'int';
    case 'Boolean': return 'bool';
    default: return fieldType;
  }
}

class Main {
  constructor(options = {}) {
    this.options = options
  }

  async quit(msg) {
    console.log(
      `end: ${moment().format('YYYY-MM-DD HH:mm:ss')} msg: ${msg} costTime: ${(moment().valueOf()
        - allStartTimeStamp)
        / 1000}S`,
    );
    process.exit(1);
  }

  async start() {
    console.log(`start: ${moment().format('YYYY-MM-DD HH:mm:ss')}`);
    allStartTimeStamp = moment().valueOf();

    /* eg: const source = `
    model User {
      id   Int    @id @default(autoincrement())
      name String @unique
    }
    `*/
    const source = fs.readFileSync('../../schema.prisma', 'utf8');

    const schema = getSchema(source);

    // 整理出来一个tableList
    const tableObj = {};
    schema.list.forEach((l) => {
      if (l.type === 'model') {
        const table = {
          name: l.name,
          columns: [],
        }
        l.properties.forEach((p) => {
          if (p.type === 'field') {
            table.columns.push(p);
          }
          return p;
        })
        tableObj[l.name] = table;
      }
      return l;
    })

    const allTable = Object.keys(tableObj);
    // 生成router/router_gen.go
    await this.generatedRouter(allTable);
    // this.quit(allTable);

    // 整理成tagle.go.template所需要的类型
    const allTableList = []
    Object.values(tableObj).forEach((t) => {
      const table = {
        lName: toFirstLowerCase(t.name), // 首字母小写名称
        name: t.name,
        mustColumns: [], // 必填字端
        optionColumns: [], // 选填字端
        manyColumns: [], // 1:N的1 N:N
        oneColumns: [], // 1:N的N
      };
      t.columns.forEach((c) => {
        c.swaggerFieldType = parseSwaggerFieldType(c.fieldType);
        c.uName = toFirstUpperCase(c.name);
        if (c.array) { // 复数字段，非实际字段
          c.one2Many = false;
          if (isOne2Many(c, t.name, tableObj)) {
            c.one2Many = true; // 判断是否为1:N的many关系
          }
          table.manyColumns.push(c);
        } else if(!c.array && allTable.includes(c.fieldType)) {
          table.oneColumns.push(c);
        } else if (!['createdAt', 'updatedAt', 'id'].includes(c.name)) {
          c.many2One = false;
          if (isMany2One(c, t)) {
            c.many2One = true; // 判断是否为N:1的1关系
          }
          if (!c.optional) {
            table.mustColumns.push(c);
          } else {
            table.optionColumns.push(c);
          }
        }
      })
      allTableList.push(table);
    })
    // this.quit(JSON.stringify(allTableList));

    // 遍历生成controller/generated/[table].go
    for(let i = 0; i < allTableList.length; i++) {
      const table = allTableList[i];
      console.log(`start: ${table.name}`);
      await this.generatedTable(table);
      console.log(`end`);
    }
    this.quit('SUCC');
  }

  async generatedTable(table) {
    const templatePath = path.resolve(__dirname, './template/table.go.template');
    const fileContent = template(templatePath, {
      table
    });
    // 消除swaggle注释的空行问题，以防swaggle解析后借口不能用的问题
    const fileArr = fileContent.split("\n");
    const spliceIndexArr = [];
    for (let i = 1; i < fileArr.length - 1; i++) {
      if (fileArr[i-1].slice(0, 4) === '// @' && fileArr[i+1].slice(0, 4) === '// @' && fileArr[i] === "") {
        spliceIndexArr.push(i);
      }
    }
    spliceIndexArr.forEach((index, i) => {
      fileArr.splice(index - i, 1);
    })
    console.log(spliceIndexArr);
    // const filePath = path.resolve(`./test/${toFirstLowerCase(table.name)}.go`);
    const filePath = path.resolve(`../../controller/generated/${toFirstLowerCase(table.name)}.go`);
    fs.writeFileSync(filePath, fileArr.join("\n"));
  }

  // 生成router/router_gen.go
  async generatedRouter(allTable) {
    const templatePath = path.resolve(__dirname, './template/router_gen.go.template');
    const fileContent = template(templatePath, {
      allTable
    });

    // const filePath = path.resolve(`./test/router_gen.go`);
    const filePath = path.resolve('../../router/router_gen.go');
    fs.writeFileSync(filePath, fileContent);
  }
}

const main = new Main();
main.start();
