package qx

import (
	"database/sql"
)

type Tx struct {
	tx     *sql.Tx
	driver Driver
	core   *Core
}

func (s *Tx) Commit() error {
	return s.tx.Commit()
}

func (s *Tx) Rollback() error {
	return s.tx.Rollback()
}

func (s *Tx) Close(perr *error) {
	err := *perr
	if err != nil {
		s.tx.Rollback()
	}
}

// Prepare Не обрабатывает 'IN (?)' специальным образом. То есть в args для такого
// плейсхолдера ожидается 1 аргумент простого типа.
func (s *Tx) Prepare(query string) (_ *Stmt, err error) {
	adaptedQuery := s.core.AdaptQueryTextOnly(query)
	stmt, err := s.tx.Prepare(adaptedQuery)
	if err != nil {
		//  FIX error
		return
	}

	return &Stmt{
		query: adaptedQuery,
		stmt:  stmt,
	}, nil
}

// Prepare Не обрабатывает 'IN (?)' специальным образом. То есть в args для такого
// плейсхолдера ожидается 1 аргумент простого типа.
func (s *Tx) PrepareInsert(seq, query string) (_ *InsertStmt, err error) {
	adaptedQuery := s.core.AdaptQueryTextOnly(query)
	stmt, err := s.tx.Prepare(adaptedQuery)
	if err != nil {
		//  FIX error
		return
	}

	return &InsertStmt{
		seq:    seq,
		query:  adaptedQuery,
		stmt:   stmt,
		tx:     s.tx,
		driver: s.driver,
	}, nil
}

// Полная поддержка IN
func (s *Tx) Exec(query string, args ...interface{}) (err error) {
	query, args, err = s.core.AdaptQuery(query, args)
	if err != nil {
		return
	}
	_, err = s.tx.Exec(query, args...)
	return
}

// Полная поддержка IN
func (s *Tx) ExecInsert(seq string, query string, args ...interface{}) (id int64, err error) {
	query, args, err = s.core.AdaptQuery(query, args)
	if err != nil {
		return
	}
	res, err := s.tx.Exec(query, args...)
	if err != nil {
		return
	}
	return s.driver.LastInsertId(s.tx, res, seq)
}

func (s *Tx) List(list interface{}) (err error) {
	return s.core.List(s.tx.Query, list)
}

func (s *Tx) ListBy(list interface{}, querySuffix string, args ...interface{}) (err error) {
	return s.core.ListBy(s.tx.Query, list, querySuffix, args)
}

func (s *Tx) ListQuery(list interface{}, rawQuery string, args ...interface{}) (err error) {
	return s.core.ListQuery(s.tx.Query, list, rawQuery, args)
}

func (s *Tx) OneBy(one interface{}, querySuffix string, args ...interface{}) (found bool, err error) {
	return s.core.OneBy(s.tx.QueryRow, one, querySuffix, args)
}

func (s *Tx) One(one interface{}) (found bool, err error) {
	return s.core.One(s.tx.QueryRow, one)
}

func (s *Tx) OneQuery(one interface{}, rawQuery string, args ...interface{}) (found bool, err error) {
	return s.core.OneQuery(s.tx.QueryRow, one, rawQuery, args)
}

func (s *Tx) Has(table string, where string, args ...interface{}) (has bool, err error) {
	return s.core.Has(hasIn{
		Table:        table,
		Where:        where,
		Args:         args,
		QueryRowFunc: s.tx.QueryRow,
	})
}

func (s *Tx) Insert(obj interface{}) (id int64, err error) {
	return s.core.Insert(s.tx, obj)
}

func (s *Tx) Update(obj interface{}) (err error) {
	return s.core.Update(s.tx, obj)
}

func (s *Tx) Remove(obj interface{}) (err error) {
	return s.core.Remove(s.tx, obj)
}

// Для для информативных ошибок Prepared запросов

type Stmt struct {
	query string
	stmt  *sql.Stmt
}

func (s *Stmt) Exec(args ...interface{}) (err error) {
	_, err = s.stmt.Exec(args...)
	if err != nil {
		return QueryError{
			query:  s.query,
			params: args,
			err:    err,
		}
	}
	return
}

func (s *Stmt) Close() error {
	return s.stmt.Close()
}

type InsertStmt struct {
	seq    string
	query  string
	stmt   *sql.Stmt
	driver Driver
	tx     *sql.Tx
}

func (s *InsertStmt) Insert(args ...interface{}) (id int64, err error) {
	res, err := s.stmt.Exec(args...)
	if err != nil {
		err = QueryError{
			query:  s.query,
			params: args,
			err:    err,
		}
		return
	}
	return s.driver.LastInsertId(s.tx, res, s.seq)
}

func (s *InsertStmt) Close() error {
	return s.stmt.Close()
}
