// integration-sql is a test-only SQL probe for repository-owned integration
// gates. It is intentionally not part of any Runtime or backend composition.
package main

import (
	"context"
	"database/sql"
	"errors"
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"strings"

	"github.com/go-sql-driver/mysql"
)

func main() {
	if err := run(os.Args[1:], os.Stdout, os.Stderr); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(args []string, stdout, stderr io.Writer) error {
	flags := flag.NewFlagSet("integration-sql", flag.ContinueOnError)
	flags.SetOutput(stderr)
	database := flags.String("database", "", "database name")
	statement := flags.String("sql", "", "SQL statement")
	if err := flags.Parse(args); err != nil {
		return err
	}
	if flags.NArg() != 1 {
		return errors.New("usage: integration-sql [--database name] [--sql statement] query|exec|reset-database")
	}

	action := flags.Arg(0)
	if action != "query" && action != "exec" && action != "reset-database" {
		return fmt.Errorf("unknown action %q (expected query, exec, or reset-database)", action)
	}
	if action == "reset-database" {
		if *statement != "" || *database == "" {
			return errors.New("reset-database requires --database and does not accept --sql")
		}
		return resetDatabase(context.Background(), *database)
	}
	if *statement == "" {
		return fmt.Errorf("%s requires --sql", action)
	}

	db, err := openDatabase(*database)
	if err != nil {
		return err
	}
	defer db.Close()

	switch action {
	case "query":
		return query(context.Background(), db, *statement, stdout)
	case "exec":
		_, err := db.ExecContext(context.Background(), *statement)
		return err
	default:
		return errors.New("unreachable action")
	}
}

func resetDatabase(ctx context.Context, name string) error {
	if !safeIdentifier(name) {
		return fmt.Errorf("unsafe database identifier %q", name)
	}
	db, err := openDatabase("")
	if err != nil {
		return err
	}
	defer db.Close()

	quoted := "`" + name + "`"
	if _, err := db.ExecContext(ctx, "DROP DATABASE IF EXISTS "+quoted); err != nil {
		return fmt.Errorf("drop database %q: %w", name, err)
	}
	if _, err := db.ExecContext(ctx, "CREATE DATABASE "+quoted); err != nil {
		return fmt.Errorf("create database %q: %w", name, err)
	}
	return nil
}

func openDatabase(database string) (*sql.DB, error) {
	config := mysql.NewConfig()
	host := os.Getenv("TIDB_HOST")
	port := os.Getenv("TIDB_PORT")
	if host == "" || port == "" || os.Getenv("TIDB_USER") == "" {
		return nil, errors.New("TIDB_HOST, TIDB_PORT, and TIDB_USER must be set")
	}
	config.Net = "tcp"
	config.Addr = net.JoinHostPort(host, port)
	config.User = os.Getenv("TIDB_USER")
	config.Passwd = os.Getenv("TIDB_PASSWORD")
	config.DBName = database
	config.ParseTime = true

	db, err := sql.Open("mysql", config.FormatDSN())
	if err != nil {
		return nil, fmt.Errorf("open TiDB connection: %w", err)
	}
	db.SetMaxOpenConns(1)
	db.SetMaxIdleConns(1)
	return db, nil
}

func query(ctx context.Context, db *sql.DB, statement string, stdout io.Writer) error {
	rows, err := db.QueryContext(ctx, statement)
	if err != nil {
		return err
	}
	defer rows.Close()

	columns, err := rows.Columns()
	if err != nil {
		return err
	}
	values := make([]any, len(columns))
	pointers := make([]any, len(columns))
	for i := range values {
		pointers[i] = &values[i]
	}
	for rows.Next() {
		if err := rows.Scan(pointers...); err != nil {
			return err
		}
		for i, value := range values {
			if i > 0 {
				if _, err := io.WriteString(stdout, "\t"); err != nil {
					return err
				}
			}
			if _, err := io.WriteString(stdout, batchValue(value)); err != nil {
				return err
			}
		}
		if _, err := io.WriteString(stdout, "\n"); err != nil {
			return err
		}
	}
	return rows.Err()
}

func batchValue(value any) string {
	if value == nil {
		return "NULL"
	}
	if bytes, ok := value.([]byte); ok {
		value = string(bytes)
	}
	return strings.NewReplacer(
		"\\", "\\\\",
		"\x00", "\\0",
		"\n", "\\n",
		"\r", "\\r",
		"\t", "\\t",
	).Replace(fmt.Sprint(value))
}

func safeIdentifier(value string) bool {
	if value == "" || len(value) > 64 {
		return false
	}
	for _, r := range value {
		if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && r != '_' {
			return false
		}
	}
	return true
}
