package store

import (
	"database/sql"
	"fmt"
	"time"

	// mysql.ParseDSN is used to structurally rewrite the DSN for UTC.
	"github.com/go-sql-driver/mysql"

	"github.com/wangjia/pangolin/server/internal/config"
	"github.com/wangjia/pangolin/server/internal/db"
)

// Open is the canonical database entry point for the Pangolin server. It
// dispatches on cfg.Driver:
//
//   - sqlite: opens the file/in-memory DB (UTC is native; no session assertion).
//   - mysql (default): structurally overrides the DSN for UTC (ParseTime, Loc,
//     Collation, time_zone), opens the pool, and asserts
//     SELECT @@session.time_zone = "+00:00" (startup-fatal if the server ignores
//     our UTC override — e.g. SQL mode forces a different TZ).
//
// The actual connection/pool/ping lives in internal/db; this layer adds only the
// MySQL-specific UTC rigor.
func Open(cfg *config.Config) (*sql.DB, error) {
	if db.Normalize(cfg.Driver) == "sqlite" {
		return db.OpenDriver("sqlite", cfg.DSN)
	}

	dsn, err := buildDSN(cfg)
	if err != nil {
		return nil, fmt.Errorf("store.Open: %w", err)
	}

	database, err := db.OpenDriver("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("store.Open: %w", err)
	}

	// Hard assertion: the session time_zone must be "+00:00".
	// This catches MySQL servers that ignore client-supplied time_zone params.
	var tz string
	if err := database.QueryRow("SELECT @@session.time_zone").Scan(&tz); err != nil {
		_ = database.Close()
		return nil, fmt.Errorf("store.Open: query @@session.time_zone: %w", err)
	}
	if tz != "+00:00" {
		_ = database.Close()
		return nil, fmt.Errorf(
			"store.Open: session time_zone=%q, want +00:00; UTC DSN override failed",
			tz,
		)
	}

	return database, nil
}

// buildDSN parses cfg.DSN and structurally overrides UTC and utf8mb4
// parameters regardless of what the caller supplied.
// This is a pure function and is tested independently.
func buildDSN(cfg *config.Config) (string, error) {
	dsnCfg, err := mysql.ParseDSN(cfg.DSN)
	if err != nil {
		return "", fmt.Errorf("buildDSN: parse DSN: %w", err)
	}

	// Force UTC — overwrite any caller-supplied values (structural, not string).
	dsnCfg.ParseTime = true
	dsnCfg.Loc = time.UTC
	// Collation implicitly sets charset to utf8mb4 via SET NAMES.
	dsnCfg.Collation = "utf8mb4_unicode_ci"
	if dsnCfg.Params == nil {
		dsnCfg.Params = make(map[string]string)
	}
	// The time_zone connection parameter sends SET time_zone = '+00:00' on connect.
	dsnCfg.Params["time_zone"] = "'+00:00'"

	return dsnCfg.FormatDSN(), nil
}
