package dbi

import (
	"database/sql"
	"errors"
	"io"
	"mayfly-go/internal/db/dbm/sqlparser"
	"mayfly-go/internal/db/dbm/sqlparser/pgsql"
	"strings"

	pq "gitee.com/liuzongyang/libpq"
)

const DefaultQuoter = `"`

const (
	// -1. 无操作
	DuplicateStrategyNone = -1
	// 1. 忽略
	DuplicateStrategyIgnore = 1
	// 2. 更新
	DuplicateStrategyUpdate = 2
)

type DbCopyTable struct {
	Id        uint64 `json:"id"`
	Db        string `json:"db" `
	TableName string `json:"tableName"`
	CopyData  bool   `json:"copyData"` // 是否复制数据
}

// BaseDialect 基础dialect，在DefaultDialect 都有默认的实现方法
type BaseDialect interface {

	// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
	// used as part of an SQL statement.  For example:
	//
	//	tblname := "my_table"
	//	data := "my_data"
	//	quoted := quoteIdentifier(tblname, '"')
	//	err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data)
	//
	// Any double quotes in name will be escaped.  The quoted identifier will be
	// case sensitive when used in a query.  If the input string contains a zero
	// byte, the result will be truncated immediately before it.
	QuoteIdentifier(name string) string

	RemoveQuote(name string) string

	// QuoteEscape 引号转义，多用于sql注释转义，防止拼接sql报错，如： comment xx is '注''释'   最终注释文本为:  注'释
	QuoteEscape(str string) string

	// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
	// to DDL and other statements that do not accept parameters) to be used as part
	// of an SQL statement.  For example:
	//
	//	exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
	//	err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
	//
	// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
	// replaced by two backslashes (i.e. "\\") and the C-style escape identifier
	QuoteLiteral(literal string) string

	// GetDbProgram 获取数据库程序模块，用于数据库备份与恢复
	GetDbProgram() (DbProgram, error)

	// GetColumnHelper
	GetColumnHelper() ColumnHelper

	// GetDumpHeler
	GetDumpHelper() DumpHelper

	// GetDataHelper 获取数据处理助手 用于解析格式化列数据等
	GetDataHelper() DataHelper

	// GetSQLParser 获取sql解析器
	GetSQLParser() sqlparser.SqlParser
}

// -----------------------------------元数据接口定义------------------------------------------
// Dialect 数据库方言 用于生成sql、批量插入等各个数据库方言不一致的实现方式
type Dialect interface {
	BaseDialect

	// BatchInsert 批量insert数据
	BatchInsert(tx *sql.Tx, tableName string, columns []string, values [][]any, duplicateStrategy int) (int64, error)

	// CopyTable 拷贝表
	CopyTable(copy *DbCopyTable) error

	// GenerateTableDDL 生成建表ddl
	GenerateTableDDL(columns []Column, tableInfo Table, dropBeforeCreate bool) []string

	// GenerateIndexDDL 生成索引ddl
	GenerateIndexDDL(indexs []Index, tableInfo Table) []string

	// UpdateSequence 有些数据库迁移完数据之后，需要更新表自增序列为当前表最大值
	UpdateSequence(tableName string, columns []Column)
}

// DefaultDialect 默认实现，若需要覆盖，则由各个数据库dialect实现去覆盖重写
type DefaultDialect struct {
}

var _ (BaseDialect) = (*DefaultDialect)(nil)

func (dx *DefaultDialect) QuoteIdentifier(name string) string {
	end := strings.IndexRune(name, 0)
	if end > -1 {
		name = name[:end]
	}
	return DefaultQuoter + strings.Replace(name, DefaultQuoter, DefaultQuoter+DefaultQuoter, -1) + DefaultQuoter
}

func (dx *DefaultDialect) RemoveQuote(name string) string {
	return strings.ReplaceAll(name, DefaultQuoter, "")
}

func (dd *DefaultDialect) QuoteEscape(str string) string {
	return strings.Replace(str, `'`, `''`, -1)
}

func (dd *DefaultDialect) QuoteLiteral(literal string) string {
	return pq.QuoteLiteral(literal)
}

func (dd *DefaultDialect) UpdateSequence(tableName string, columns []Column) {}

// GetDbProgram 获取数据库程序模块，用于数据库备份与恢复
func (dd *DefaultDialect) GetDbProgram() (DbProgram, error) {
	return nil, errors.New("not support db program")
}

func (dd *DefaultDialect) GetDumpHelper() DumpHelper {
	return new(DefaultDumpHelper)
}

func (dd *DefaultDialect) GetColumnHelper() ColumnHelper {
	return new(DefaultColumnHelper)
}

func (pd *DefaultDialect) GetSQLParser() sqlparser.SqlParser {
	return new(pgsql.PgsqlParser)
}

func (pd *DefaultDialect) GetDataHelper() DataHelper {
	return nil
}

// ColumnHelper 数据库迁移辅助方法
type ColumnHelper interface {
	// ToCommonColumn 数据库方言自带的列转换为公共列
	ToCommonColumn(dialectColumn *Column)

	// ToColumn 公共列转为各个数据库方言自带的列
	ToColumn(commonColumn *Column)

	// FixColumn 根据数据库类型修复字段长度、精度等
	FixColumn(column *Column)
}

type DefaultColumnHelper struct {
}

func (dd *DefaultColumnHelper) ToCommonColumn(dialectColumn *Column) {}

func (dd *DefaultColumnHelper) ToColumn(commonColumn *Column) {}

func (dd *DefaultColumnHelper) FixColumn(column *Column) {}

// DumpHelper 导出辅助方法
type DumpHelper interface {
	BeforeInsert(writer io.Writer, tableName string)

	BeforeInsertSql(quoteSchema string, quoteTableName string) string

	AfterInsert(writer io.Writer, tableName string, columns []Column)
}

type DefaultDumpHelper struct {
}

func (dd *DefaultDumpHelper) BeforeInsert(writer io.Writer, tableName string) {
	writer.Write([]byte("BEGIN;\n"))
}

func (dd *DefaultDumpHelper) BeforeInsertSql(quoteSchema string, quoteTableName string) string {
	return ""
}

func (dd *DefaultDumpHelper) AfterInsert(writer io.Writer, tableName string, columns []Column) {
	writer.Write([]byte("COMMIT;\n"))
}
