package qx

import (
	"database/sql"
	"fmt"
	"reflect"
	"regexp"
	"strings"
)

type oracle struct {
}

// MySQL синтаксис:
// LIMIT {offset}, {count}
// LIMIT {count}
var reLimitClause = regexp.MustCompile(`(?i)\bLIMIT\s+(?:(\d+|\?)\s*,)?\s*(\d+|\?)`)

// Заменяем на ORACLE синтаксис:
// OFFSET {offset} ROWS
// OFFSET {offset} ROWS FETCH NEXT {count} ROWS ONLY
func replaceLimitFunc(limitStr string) (str string) {
	groups := reLimitClause.FindStringSubmatch(limitStr)
	if groups[1] != "" {
		// ВАЖНО!
		// Не забываем про пробел после ROWS
		str = fmt.Sprintf("OFFSET %s ROWS ", groups[1])
	}
	str += fmt.Sprintf("FETCH NEXT %s ROWS ONLY", groups[2])
	return
}

func (s *oracle) AdaptLimitClause(query string) string {
	return reLimitClause.ReplaceAllStringFunc(query, replaceLimitFunc)
}

func (s *oracle) ModifySelectQuery(parsedList []string, inArgs []interface{}) (query string, args []interface{}, err error) {
	number := 1
	// копируем список
	list := make([]string, len(parsedList))
	copy(list, parsedList)

	//fmt.Printf("parsedList: %q\n", parsedList)

	argIdx := 0
	for idx := 1; idx < len(list); idx += 2 {
		pos := list[idx]
		if pos == "" {
			// простой параметр '?'
			list[idx] = fmt.Sprintf(":%d", number)
			number++
			args = append(args, inArgs[argIdx])
		} else {
			// Параметр из SQL 'IN' инструкции
			arg := inArgs[argIdx]
			// Проверяем аргумент - массив или нет
			params := reflect.ValueOf(arg)
			if params.Kind() != reflect.Slice {
				// простой параметр '?'
				list[idx] = "?"
				args = append(args, arg)
			} else {
				// параметр-массив
				placeholders := make([]string, params.Len())
				for i := 0; i < len(placeholders); i++ {
					placeholders[i] = fmt.Sprintf(":%d", number)
					number++
					args = append(args, params.Index(i).Interface())
				}
				list[idx] = strings.Join(placeholders, ", ")
			}
		}
		argIdx++
	}
	// Склеиваем итоговый запрос
	query = strings.Join(list, "")
	return
}

func (s *oracle) JoinSelectQuery(parsedList []string) (query string) {
	number := 1
	// копируем список
	list := make([]string, len(parsedList))
	copy(list, parsedList)

	for idx := 1; idx < len(list); idx += 2 {
		// простой параметр '?'
		list[idx] = fmt.Sprintf(":%d", number)
		number++
	}
	// Склеиваем итоговый запрос
	query = strings.Join(list, "")
	return
}

func (s *oracle) LastInsertId(tx *sql.Tx, res sql.Result, seq string) (id int64, err error) {
	err = tx.QueryRow(fmt.Sprintf(`SELECT %s.currval FROM DUAL`, seq)).Scan(&id)
	return
}
