package database

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/glebarez/sqlite"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"oci-portal/internal/model"
)

// newDBLogger 构造脱敏的 GORM 日志器(S-08):SQL 一律参数化输出,
// 实参(密码哈希 / token / 任务 payload)不落日志;业务正常路径的
// record-not-found 不打 SQL。
func newDBLogger() logger.Interface {
	return logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{
		SlowThreshold:             200 * time.Millisecond,
		LogLevel:                  logger.Warn,
		IgnoreRecordNotFoundError: true,
		ParameterizedQueries:      true,
	})
}

// Open 按驱动打开数据库并自动迁移全部模型。
// driver 取值 sqlite(默认)/mysql/postgres;sqlite 用 path,其余用 dsn。
func Open(driver, dsn, path string) (*gorm.DB, error) {
	dialector, err := buildDialector(driver, dsn, path)
	if err != nil {
		return nil, err
	}
	db, err := gorm.Open(dialector, &gorm.Config{
		Logger: newDBLogger(),
	})
	if err != nil {
		return nil, fmt.Errorf("open %s database: %w", dialector.Name(), err)
	}
	if err := autoMigrate(db); err != nil {
		return nil, fmt.Errorf("auto migrate: %w", err)
	}
	return db, nil
}

// buildDialector 把驱动名映射为 GORM dialector;三个驱动均为纯 Go,不引入 cgo。
// MySQL 设 DefaultStringSize=512:未标 size 的 string 建为 varchar(512),
// 避免 OCID / URL 类字段被默认 varchar(191) 截断(SQLite/PG 的 string 天然无长度上限)。
func buildDialector(driver, dsn, path string) (gorm.Dialector, error) {
	switch driver {
	case "", "sqlite":
		return sqlite.Open(path), nil
	case "mysql":
		return mysql.New(mysql.Config{DSN: dsn, DefaultStringSize: 512}), nil
	case "postgres":
		return postgres.Open(dsn), nil
	default:
		return nil, fmt.Errorf("unsupported DB_DRIVER %q (sqlite/mysql/postgres)", driver)
	}
}

func autoMigrate(db *gorm.DB) error {
	return db.AutoMigrate(
		&model.User{}, &model.UserIdentity{}, &model.OciConfig{}, &model.Task{}, &model.TaskLog{},
		&model.CheckSnapshot{}, &model.CostSnapshot{},
		&model.RegionCache{}, &model.CompartmentCache{}, &model.Setting{},
		&model.SystemLog{}, &model.LogEvent{}, &model.Proxy{},
		&model.AiKey{}, &model.AiChannel{}, &model.AiModelCache{}, &model.AiModelBlacklist{}, &model.AiCallLog{},
		&model.AiContentLog{},
	)
}
