package converter

import (
	"database/sql"
	"reflect"
	"time"
)

// StructToStruct 通用结构体转换函数，利用反射将源结构体的字段值复制到目标结构体
// src: 源结构体（通常是 models）
// dst: 目标结构体（通常是 pb），必须是指针
// 支持的自动转换：
//   - time.Time -> int64 (Unix 时间戳)
//   - sql.NullTime -> int64 (如果有效)
//   - sql.NullInt64 -> int64
//   - sql.NullString -> string
//   - 相同名称和兼容类型的字段
func StructToStruct(src, dst interface{}) error {
	if src == nil {
		return nil
	}

	srcVal := reflect.ValueOf(src)
	dstVal := reflect.ValueOf(dst)

	// 确保 dst 是指针
	if dstVal.Kind() != reflect.Ptr {
		return newError("destination must be a pointer")
	}

	dstVal = dstVal.Elem()

	// 如果 src 是指针，解引用
	if srcVal.Kind() == reflect.Ptr {
		srcVal = srcVal.Elem()
	}

	// 都必须是结构体
	if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
		return newError("both source and destination must be structs")
	}

	srcType := srcVal.Type()

	// 遍历源结构体的所有字段
	for i := 0; i < srcVal.NumField(); i++ {
		srcField := srcVal.Field(i)
		srcFieldName := srcType.Field(i).Name

		// 在目标结构体中查找同名字段
		dstField := dstVal.FieldByName(srcFieldName)
		if !dstField.IsValid() || !dstField.CanSet() {
			continue
		}

		// 进行类型转换和赋值
		if err := assignValue(srcField, dstField); err != nil {
			continue // 如果单个字段转换失败，继续处理其他字段
		}
	}

	return nil
}

// assignValue 尝试将源字段值赋给目标字段
func assignValue(srcField, dstField reflect.Value) error {
	// 如果是可直接赋值的类型
	if srcField.Type() == dstField.Type() {
		dstField.Set(srcField)
		return nil
	}

	srcType := srcField.Type()
	dstType := dstField.Type()

	// 处理 time.Time -> int64 的转换
	if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 {
		t := srcField.Interface().(time.Time)
		dstField.SetInt(t.Unix())
		return nil
	}

	// 处理 sql.NullTime -> int64 的转换
	if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 {
		nt := srcField.Interface().(sql.NullTime)
		if nt.Valid {
			dstField.SetInt(nt.Time.Unix())
		}
		return nil
	}

	// 处理 sql.NullTime -> time.Time 的转换
	if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) {
		nt := srcField.Interface().(sql.NullTime)
		if nt.Valid {
			dstField.Set(reflect.ValueOf(nt.Time))
		}
		return nil
	}

	// 处理 sql.NullInt64 -> int64 的转换
	if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 {
		ni := srcField.Interface().(sql.NullInt64)
		if ni.Valid {
			dstField.SetInt(ni.Int64)
		}
		return nil
	}

	// 处理 sql.NullString -> string 的转换
	if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String {
		ns := srcField.Interface().(sql.NullString)
		if ns.Valid {
			dstField.SetString(ns.String)
		}
		return nil
	}

	// 处理 sql.NullBool -> bool 的转换
	if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool {
		nb := srcField.Interface().(sql.NullBool)
		if nb.Valid {
			dstField.SetBool(nb.Bool)
		}
		return nil
	}

	// 处理 int -> int64 的转换
	if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 {
		dstField.SetInt(int64(srcField.Int()))
		return nil
	}

	// 处理 int64 -> int 的转换
	if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int {
		dstField.SetInt(srcField.Int())
		return nil
	}

	// 处理 string -> string（某些情况下可能存在复制）
	if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String {
		dstField.SetString(srcField.String())
		return nil
	}

	// 处理 bool -> bool
	if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool {
		dstField.SetBool(srcField.Bool())
		return nil
	}

	return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String())
}

// SliceToSlice 通用切片转换函数，使用 StructToStruct 转换每个元素
func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) {
	srcVal := reflect.ValueOf(src)

	// src 必须是切片
	if srcVal.Kind() != reflect.Slice {
		return nil, newError("source must be a slice")
	}

	// 获取原始 dst slice type
	dstSliceVal := reflect.ValueOf(dstSliceType)
	if dstSliceVal.Kind() != reflect.Slice {
		return nil, newError("dstSliceType must be a slice type")
	}

	dstSliceElemType := dstSliceVal.Type().Elem()

	// 创建新的目标切片
	dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len())

	// 逐个转换元素
	for i := 0; i < srcVal.Len(); i++ {
		srcElem := srcVal.Index(i)
		dstElem := reflect.New(dstSliceElemType)

		// 如果 src 元素是指针，需要解引用
		if srcElem.Kind() == reflect.Ptr {
			srcElem = srcElem.Elem()
		}

		// 转换单个元素
		dstElemIface := dstElem.Interface()
		if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil {
			return nil, err
		}

		dstSlice.Index(i).Set(dstElem.Elem())
	}

	return dstSlice.Interface(), nil
}

type Error struct {
	msg string
}

func (e *Error) Error() string {
	return e.msg
}

func newError(msg string) error {
	return &Error{msg: msg}
}
