package client

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	// List of keywords that are not allowed in read-only mode
	reRestrictedKeywords = regexp.MustCompile(`(?mi)\s?(CREATE|INSERT|UPDATE|DROP|DELETE|TRUNCATE|GRANT|OPEN|IMPORT|COPY)\s`)

	// Comment regular expressions
	reSlashComment = regexp.MustCompile(`(?m)/\*.+\*/`)
	reDashComment  = regexp.MustCompile(`(?m)--.+`)

	// Postgres version signature
	postgresSignature     = regexp.MustCompile(`(?i)postgresql ([\d\.]+)\s?`)
	postgresDumpSignature = regexp.MustCompile(`\s([\d\.]+)\s?`)
	postgresType          = "PostgreSQL"

	// Cockroach version signature
	cockroachSignature = regexp.MustCompile(`(?i)cockroachdb ccl v([\d\.]+)\s?`)
	cockroachType      = "CockroachDB"
)

// Get major and minor version components
// Example: 10.2.3.1 -> 10.2
func getMajorMinorVersion(str string) (major int, minor int) {
	chunks := strings.Split(str, ".")
	fmt.Sscanf(chunks[0], "%d", &major)
	if len(chunks) > 1 {
		fmt.Sscanf(chunks[1], "%d", &minor)
	}
	return
}

// Get short version from the string
// Example: 10.2.3.1 -> 10.2
func getMajorMinorVersionString(str string) string {
	major, minor := getMajorMinorVersion(str)
	return fmt.Sprintf("%d.%d", major, minor)
}

func detectServerTypeAndVersion(version string) (bool, string, string) {
	version = strings.TrimSpace(version)

	// Detect postgresql
	matches := postgresSignature.FindAllStringSubmatch(version, 1)
	if len(matches) > 0 {
		return true, postgresType, matches[0][1]
	}

	// Detect cockroachdb
	matches = cockroachSignature.FindAllStringSubmatch(version, 1)
	if len(matches) > 0 {
		return true, cockroachType, matches[0][1]
	}

	return false, "", ""
}

// detectDumpVersion parses out version from `pg_dump -V` command.
func detectDumpVersion(version string) (bool, string) {
	matches := postgresDumpSignature.FindAllStringSubmatch(version, 1)
	if len(matches) > 0 {
		return true, matches[0][1]
	}
	return false, ""
}

func checkVersionRequirement(client, server string) bool {
	clientMajor, clientMinor := getMajorMinorVersion(client)
	serverMajor, serverMinor := getMajorMinorVersion(server)

	if serverMajor < 10 {
		return clientMajor >= serverMajor && clientMinor >= serverMinor
	}

	return clientMajor >= serverMajor
}

// containsRestrictedKeywords returns true if given keyword is not allowed in read-only mode
func containsRestrictedKeywords(str string) bool {
	str = reSlashComment.ReplaceAllString(str, "")
	str = reDashComment.ReplaceAllString(str, "")

	return reRestrictedKeywords.MatchString(str)
}

func hasBinary(data string, checkLen int) bool {
	for idx, chr := range data {
		if int(chr) < 32 || int(chr) > 126 {
			return true
		}
		if idx >= checkLen {
			break
		}
	}
	return false
}
