package mysql

import (
	"context"
	"database/sql"
	"errors"
	"hyapp/pkg/appcode"
	"hyapp/pkg/xerr"
	resourcedomain "hyapp/services/wallet-service/internal/domain/resource"
	"strings"
	"time"
)

// ListGiftTypeConfigs 返回礼物面板 tab 类型配置；默认类型会按 app_code 自动补齐，避免新租户缺配置。
func (r *Repository) ListGiftTypeConfigs(ctx context.Context, query resourcedomain.ListGiftTypeConfigsQuery) ([]resourcedomain.GiftTypeConfig, error) {
	if r == nil || r.db == nil {
		return nil, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, query.AppCode)
	if err := r.ensureDefaultGiftTypeConfigs(ctx, r.db); err != nil {
		return nil, err
	}
	query.AppCode = appcode.FromContext(ctx)
	query = normalizeGiftTypeConfigListQuery(query)
	where, args := giftTypeConfigWhereSQL(query)
	rows, err := r.db.QueryContext(ctx, giftTypeConfigSelectSQL()+where+`
		ORDER BY sort_order DESC, type_code ASC`, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	items := make([]resourcedomain.GiftTypeConfig, 0, len(resourcedomain.DefaultGiftTypeConfigs(query.AppCode)))
	for rows.Next() {
		item, err := scanGiftTypeConfig(rows)
		if err != nil {
			return nil, err
		}
		items = append(items, item)
	}
	if err := rows.Err(); err != nil {
		return nil, err
	}
	return items, nil
}

// UpsertGiftTypeConfig 更新礼物类型 tab 展示配置；type_code 是礼物表引用的稳定业务键。
func (r *Repository) UpsertGiftTypeConfig(ctx context.Context, command resourcedomain.GiftTypeConfigCommand) (resourcedomain.GiftTypeConfig, error) {
	if r == nil || r.db == nil {
		return resourcedomain.GiftTypeConfig{}, xerr.New(xerr.Unavailable, "mysql repository is not configured")
	}
	ctx = contextWithCommandApp(ctx, command.AppCode)
	command.AppCode = appcode.FromContext(ctx)
	command = normalizeGiftTypeConfigCommand(command)
	if err := validateGiftTypeConfigCommand(command); err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}

	tx, err := r.db.BeginTx(ctx, nil)
	if err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}
	defer func() { _ = tx.Rollback() }()
	if err := r.ensureDefaultGiftTypeConfigs(ctx, tx); err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}
	nowMs := time.Now().UnixMilli()
	if _, err := tx.ExecContext(ctx, `
		INSERT INTO gift_type_configs (
			app_code, type_code, name, tab_key, status, sort_order,
			created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms
		) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			name = VALUES(name),
			tab_key = VALUES(tab_key),
			status = VALUES(status),
			sort_order = VALUES(sort_order),
			updated_by_user_id = VALUES(updated_by_user_id),
			updated_at_ms = VALUES(updated_at_ms)`,
		command.AppCode, command.TypeCode, command.Name, command.TabKey, command.Status, command.SortOrder,
		command.OperatorUserID, command.OperatorUserID, nowMs, nowMs,
	); err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}
	item, err := r.getGiftTypeConfigTx(ctx, tx, command.TypeCode)
	if err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}
	if err := tx.Commit(); err != nil {
		return resourcedomain.GiftTypeConfig{}, err
	}
	return item, nil
}

func (r *Repository) getGiftTypeConfigTx(ctx context.Context, tx *sql.Tx, typeCode string) (resourcedomain.GiftTypeConfig, error) {
	row := tx.QueryRowContext(ctx, giftTypeConfigSelectSQL()+`WHERE app_code = ? AND type_code = ?`, appcode.FromContext(ctx), typeCode)
	item, err := scanGiftTypeConfig(row)
	if errors.Is(err, sql.ErrNoRows) {
		return resourcedomain.GiftTypeConfig{}, xerr.New(xerr.NotFound, "gift type config not found")
	}
	return item, err
}

func (r *Repository) giftTypeConfigExistsTx(ctx context.Context, tx *sql.Tx, typeCode string) (bool, error) {
	var exists int
	err := tx.QueryRowContext(ctx, `
		SELECT 1
		FROM gift_type_configs
		WHERE app_code = ? AND type_code = ?
		LIMIT 1`, appcode.FromContext(ctx), typeCode).Scan(&exists)
	if errors.Is(err, sql.ErrNoRows) {
		return false, nil
	}
	return err == nil, err
}

func (r *Repository) ensureDefaultGiftTypeConfigs(ctx context.Context, execer sqlExecer) error {
	nowMs := time.Now().UnixMilli()
	for _, item := range resourcedomain.DefaultGiftTypeConfigs(appcode.FromContext(ctx)) {
		if _, err := execer.ExecContext(ctx, `
			INSERT IGNORE INTO gift_type_configs (
				app_code, type_code, name, tab_key, status, sort_order,
				created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms
			) VALUES (?, ?, ?, ?, ?, ?, 0, 0, ?, ?)`,
			item.AppCode, item.TypeCode, item.Name, item.TabKey, item.Status, item.SortOrder, nowMs, nowMs,
		); err != nil {
			return err
		}
	}
	return nil
}

func scanGiftTypeConfig(scanner scanTarget) (resourcedomain.GiftTypeConfig, error) {
	var item resourcedomain.GiftTypeConfig
	err := scanner.Scan(
		&item.AppCode,
		&item.TypeCode,
		&item.Name,
		&item.TabKey,
		&item.Status,
		&item.SortOrder,
		&item.CreatedByUserID,
		&item.UpdatedByUserID,
		&item.CreatedAtMS,
		&item.UpdatedAtMS,
	)
	return item, err
}

func giftTypeConfigSelectSQL() string {
	return `
		SELECT app_code, type_code, name, tab_key, status, sort_order,
		       created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms
		FROM gift_type_configs
		`
}

func giftTypeConfigWhereSQL(query resourcedomain.ListGiftTypeConfigsQuery) (string, []any) {
	where := `WHERE app_code = ?`
	args := []any{query.AppCode}
	if query.ActiveOnly {
		where += ` AND status = 'active'`
	} else if query.Status != "" {
		where += ` AND status = ?`
		args = append(args, query.Status)
	}
	return where, args
}

func normalizeGiftTypeConfigListQuery(query resourcedomain.ListGiftTypeConfigsQuery) resourcedomain.ListGiftTypeConfigsQuery {
	query.Status = strings.ToLower(strings.TrimSpace(query.Status))
	if query.Status != "" && !resourcedomain.ValidStatus(query.Status) {
		query.Status = "__invalid__"
	}
	return query
}

func normalizeGiftTypeConfigCommand(command resourcedomain.GiftTypeConfigCommand) resourcedomain.GiftTypeConfigCommand {
	command.TypeCode = resourcedomain.NormalizeGiftTypeCode(command.TypeCode)
	command.Name = strings.TrimSpace(command.Name)
	command.TabKey = resourcedomain.NormalizeGiftTypeTabKey(command.TabKey)
	command.Status = resourcedomain.NormalizeStatus(command.Status)
	return command
}

func validateGiftTypeConfigCommand(command resourcedomain.GiftTypeConfigCommand) error {
	if !resourcedomain.ValidGiftTypeCode(command.TypeCode) || command.Name == "" || !resourcedomain.ValidGiftTypeTabKey(command.TabKey) {
		return xerr.New(xerr.InvalidArgument, "gift type config is incomplete")
	}
	if !resourcedomain.ValidStatus(command.Status) {
		return xerr.New(xerr.InvalidArgument, "status is invalid")
	}
	return nil
}
