package schema

import (
	"context"
	"crypto/sha256"
	"database/sql"
	"encoding/hex"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"unicode"
)

var dbNameRe = regexp.MustCompile(`^[a-z][a-z0-9_]{1,47}$`)

type Runner interface {
	ExecDDL(ctx context.Context, stmts []string) error
	EnsureDatabase(ctx context.Context, dbName string) error
}

type NoopRunner struct{}

func (NoopRunner) ExecDDL(context.Context, []string) error   { return nil }
func (NoopRunner) EnsureDatabase(context.Context, string) error { return nil }

type PostgresRunner struct {
	DB          *sql.DB
	AdminDSN    string // 用于 CREATE DATABASE（连到 postgres 库）
}

func (r *PostgresRunner) ExecDDL(ctx context.Context, stmts []string) error {
	tx, err := r.DB.BeginTx(ctx, nil)
	if err != nil {
		return err
	}
	defer func() { _ = tx.Rollback() }()
	for _, s := range stmts {
		if _, err := tx.ExecContext(ctx, s); err != nil {
			return fmt.Errorf("ddl failed: %w\nsql: %s", err, s)
		}
	}
	return tx.Commit()
}

func (r *PostgresRunner) EnsureDatabase(ctx context.Context, dbName string) error {
	if !dbNameRe.MatchString(dbName) {
		return fmt.Errorf("invalid database name: %s", dbName)
	}
	admin := r.DB
	var err error
	if r.AdminDSN != "" {
		admin, err = sql.Open("postgres", r.AdminDSN)
		if err != nil {
			return err
		}
		defer admin.Close()
	}
	var exists bool
	if err := admin.QueryRowContext(ctx,
		`SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname=$1)`, dbName,
	).Scan(&exists); err != nil {
		return err
	}
	if exists {
		return nil
	}
	// CREATE DATABASE 不能在事务中
	_, err = admin.ExecContext(ctx, fmt.Sprintf(`CREATE DATABASE %s`, quoteIdent(dbName)))
	return err
}

// DSNForDatabase 把原 DSN 的库名替换为目标库。
func DSNForDatabase(baseDSN, dbName string) (string, error) {
	if !dbNameRe.MatchString(dbName) {
		return "", fmt.Errorf("invalid database name")
	}
	u, err := url.Parse(baseDSN)
	if err != nil {
		return "", err
	}
	u.Path = "/" + dbName
	return u.String(), nil
}

func QuoteIdentExport(name string) string { return quoteIdent(name) }

func SanitizeDBName(tenantID int64, slug string) string {
	name := fmt.Sprintf("appdb_t%d_%s", tenantID, slug)
	name = strings.ToLower(name)
	if len(name) > 48 {
		name = name[:48]
	}
	return name
}

// ValidDBName 是否可作为 Postgres 物理库名（CREATE DATABASE）。
func ValidDBName(dbName string) bool {
	return dbNameRe.MatchString(strings.TrimSpace(dbName))
}

// SafePhysicalDBName 优先用 preferred；非法则用 fallback；仍非法则 user_fallback。
func SafePhysicalDBName(preferred, fallback string) string {
	p := strings.TrimSpace(preferred)
	if ValidDBName(p) {
		return p
	}
	f := strings.TrimSpace(fallback)
	if ValidDBName(f) {
		return f
	}
	return "appdb_default"
}

// SafeSyncDatabaseName Z35：智能体同步落点的 Postgres 物理库名。
// - 已是合法 ASCII → 原样
// - 中文/非法串：有 userID → user_{id}（昵称可变仍稳定）；否则对标签做稳定哈希 db{hex}
// - 展示中文请写 Binding.DisplayName / Binding.DatabaseName（可读名），勿把物理名当展示名
func SafeSyncDatabaseName(preferred string, agentID, userID int64) string {
	p := strings.TrimSpace(preferred)
	if ValidDBName(p) {
		return p
	}
	// 有登录用户：物理名跟用户稳定绑定；中文昵称只作展示
	if userID > 0 {
		n := fmt.Sprintf("user_%d", userID)
		if ValidDBName(n) {
			return n
		}
	}
	if p != "" {
		return HashLabelDBName(p, agentID)
	}
	if agentID > 0 {
		n := fmt.Sprintf("agent_%d", agentID)
		if ValidDBName(n) {
			return n
		}
	}
	return "appdb_default"
}

// HashLabelDBName 将任意标签（含中文）稳定映射为合法物理库名：db + 16 hex。
func HashLabelDBName(label string, agentID int64) string {
	label = strings.TrimSpace(label)
	sum := sha256.Sum256([]byte(fmt.Sprintf("ajz-db:%d:%s", agentID, label)))
	return "db" + hex.EncodeToString(sum[:8])
}

// IsDisplayLabel 是否更像「展示名」而非物理库名（含非 ASCII / 大写 / 连字符等）。
func IsDisplayLabel(s string) bool {
	s = strings.TrimSpace(s)
	if s == "" || ValidDBName(s) {
		return false
	}
	for _, r := range s {
		if r > unicode.MaxASCII || unicode.Is(unicode.Han, r) {
			return true
		}
	}
	return true
}
