#!/bin/bash

# 脚本功能：
# 1. 根据传入的表名和应用名，连接指定数据库，生成对应表的模型代码（使用gentool工具，需确保该工具已正确安装并可用）。
# 2. 生成对应表的领域实体Bo代码，其字段和模型字段保持一致，并定义相关的转换等方法（简单示例）。
# 3. 在指定目录下创建对应表的CRUD操作代码文件，定义针对该表的常见增删改查以及列表查询、按字段查询等操作方法。

# 设置数据库连接信息，注意密码部分如果包含特殊字符可能需要进行转义处理，这里示例中未做额外处理，需根据实际情况检查
dsn="root:lsxddb123.@tcp(47.108.53.72:3306)/voucher?parseTime=True&loc=Local"
# 获取当前脚本所在的绝对路径，即使脚本被在不同目录下调用也能正确定位相关文件和目录
SHELL_FOLDER=$(cd "$(dirname "\$0")" || exit; pwd)
echo "$SHELL_FOLDER"

# 获取传入的表名和应用名参数，如果没有传入参数则给出提示并退出脚本
if [ $# -lt 1 ]; then
    echo "请传入要生成代码的表名和应用名作为参数"
    exit 1
fi

table=$1

dir_path="${SHELL_FOLDER}"

model_path="${dir_path}/internal/data/model"
repo_impl_path="${dir_path}/internal/data/repoimpl"
repo_path="${dir_path}/internal/biz/repo"
bo_path="${dir_path}/internal/biz/bo"

# 处理表名，将空格和下划线分隔的部分转换为大驼峰命名
table_capitalized=$(echo "$table" | tr -s '[:space:]_' '\n' | awk '{print toupper(substr($0,1,1)) substr($0,2)}' | tr -d '\n')

# 检查转换后的字符串是否为空，如果为空则表示可能输入的表名不符合预期格式
if [ -z "$table_capitalized" ]; then
    echo "输入的表名格式不正确，请检查是否包含非法字符或仅为空格/下划线"
    exit 1
fi
# 输出大驼峰命名后的字符串
echo "转换后的大驼峰命名字符串为 ${table_capitalized}"

# 检查要生成的模型代码文件是否已存在，如果存在则提示用户并退出脚本，避免覆盖已有文件造成意外情况
if [ -f "${model_path}/${table_capitalized}.gen.go" ]; then
    echo "文件已存在，如果要重新生成，请先删除文件：rm -f ${model_path}/${table_capitalized}.gen.go"
    exit 1
fi

# 生成模型代码部分
echo "正在生成模型代码..."
gentool -dsn="${dsn}" -tables="${table}" -onlyModel -modelPkgName="model" -outPath="${model_path}"
echo "模型代码生成完成：${model_path}/${table}.gen.go"

# 解析模型文件，获取字段信息（使用awk或sed等工具）
model_file="${model_path}/${table}.gen.go"

# 假设模型代码中字段定义遵循一定格式，且字段类型简单，可以从文件中提取字段名称和类型
# 在实际情况中，这段提取代码可能需要根据模型文件的实际格式调整
fields=$(awk '/type.*struct/ {flag=1} flag && /}/ {flag=0} flag' "$model_file" | grep -E '^[[:space:]]+[a-zA-Z_]+[[:space:]]+[a-zA-Z_0-9]+[[:space:]]*' | sed -E 's/^[[:space:]]+([a-zA-Z_]+)[[:space:]]+([a-zA-Z_0-9]+).*/\1 \2/' )

# 生成领域实体Bo代码部分
echo "正在生成领域实体Bo代码..."
bo_file="${bo_path}/${table}_bo.go"
mkdir -p "${bo_path}"

# 使用heredoc方式生成Bo结构体代码，使其字段和模型字段一致
cat > "${bo_file}" <<EOL
package bo

import "time"

// ${table_capitalized}Bo 领域实体Bo结构，字段和模型字段保持一致
type ${table_capitalized}Bo struct {
EOL

# 遍历字段信息并动态生成Bo字段
while IFS=" " read -r field_name field_type; do
    # 使用 type 字段类型来决定Bo中的字段类型，简单处理，如果有更多复杂情况可以扩展
    case "$field_type" in
        *int*)   field_type="int32" ;;
        *uint*)  field_type="uint32" ;;
        *string*) field_type="string" ;;
        *float*) field_type="float64" ;;
        *bool*)  field_type="bool" ;;
        *time*)  field_type="time.Time" ;;
        *gorm.Model*)  field_type="gorm.Model" ;;
        *) ;;
    esac

    # 输出字段到Bo文件
    echo "    ${field_name} ${field_type}" >> "${bo_file}"
done <<< "$fields"

# 结束Bo文件
echo "}" >> "${bo_file}"

# 检查领域实体Bo代码文件是否成功生成，如果文件不存在则提示生成失败并退出脚本
if [ ! -f "${bo_file}" ]; then
    echo "领域实体Bo代码生成失败，请检查相关权限或磁盘空间等问题"
    exit 1
fi
echo "领域实体Bo代码生成完成：${bo_file}"

# 生成 CRUD 操作代码部分
echo "正在生成 CRUD 操作代码..."

repo_file="${repo_path}/${table}.go"
mkdir -p "${repo_path}"
cat > "${repo_file}" <<EOL
package repo

import (
	  "context"
    "voucher/internal/biz/bo"
)

type ${table_capitalized}Repo interface {
  // Create 创建 ${table_capitalized}
  Create(ctx context.Context, req *bo.${table_capitalized}Bo) (*bo.${table_capitalized}Bo, error)
  // GetByID 根据 ID 获取 ${table_capitalized}
  GetByID(ctx context.Context,id int32) (*bo.${table_capitalized}Bo, error)
}
EOL


crud_file="${repo_impl_path}/${table}.go"
mkdir -p "${repo_impl_path}"

# 使用heredoc方式向文件中写入CRUD操作代码，定义了针对指定表的各种数据库操作方法，包括创建、根据ID获取、更新、删除、列表查询以及按字段查询等功能
cat > "${crud_file}" <<EOL
package repoimpl

import (
	  "context"
    "voucher/internal/data/model"
    "voucher/internal/biz/repo"
    "voucher/internal/biz/bo"
    "voucher/internal/data"
    err2 "voucher/api/err"
)

// ${table_capitalized}RepoImpl .
type ${table_capitalized}RepoImpl struct {
   	Base[model.${table_capitalized}, bo.${table_capitalized}Bo]
   	db *data.Db
}

// New${table_capitalized}RepoImpl .
func New${table_capitalized}RepoImpl() repo.${table_capitalized}Repo {
    return &${table_capitalized}RepoImpl{}
}

func (r *${table_capitalized}RepoImpl) DB(ctx context.Context) *gorm.DB {
	return p.db.DB(ctx).WithContext(ctx).Model(model.${table_capitalized}{})
}

func (r *${table_capitalized}RepoImpl) Create(ctx context.Context, req *bo.${table_capitalized}Bo) (*bo.${table_capitalized}Bo, error) {
	// todo 待实现
	return nil, nil
}

// GetByID 根据 ID 获取 ${table_capitalized}
func (r *${table_capitalized}RepoImpl) GetByID(ctx context.Context,id int32) (*bo.${table_capitalized}Bo, error) {
    var item model.${table_capitalized}

    tx := p.DB(ctx).Where(model.${table_capitalized}{ID: id}).First(&item)

    if tx.Error != nil {
      return nil, fmt.Errorf("b fail %w", tx.Error)
    }

    if tx.RowsAffected == 0 {
      return nil, err2.ErrorDbNotFound("数据不存在")
    }

    return r.ToBo(&item), nil
}
EOL

# 检查CRUD操作代码文件是否成功生成，如果文件不存在则提示生成失败并退出脚本
if [ ! -f "${crud_file}" ]; then
    echo "CRUD操作代码生成失败，请检查相关权限或磁盘空间等问题"
    exit 1
fi
echo "CRUD操作代码生成完成：${crud_file}"

# 输出完成消息，告知用户模型、领域实体Bo和CRUD代码生成完毕
echo "模型、领域实体Bo和 CRUD 代码生成完毕！"
