package pg_test

import (
	"strings"

	"github.com/go-pg/pg"

	. "gopkg.in/check.v1"
)

var _ = Suite(&TxTest{})

type TxTest struct {
	db *pg.DB
}

func (t *TxTest) SetUpTest(c *C) {
	t.db = pg.Connect(pgOptions())
}

func (t *TxTest) TearDownTest(c *C) {
	c.Assert(t.db.Close(), IsNil)
}

func (t *TxTest) TestMultiPrepare(c *C) {
	tx, err := t.db.Begin()
	c.Assert(err, IsNil)

	stmt1, err := tx.Prepare(`SELECT 'test_multi_prepare_tx1'`)
	c.Assert(err, IsNil)

	stmt2, err := tx.Prepare(`SELECT 'test_multi_prepare_tx2'`)
	c.Assert(err, IsNil)

	var s1 string
	_, err = stmt1.QueryOne(pg.Scan(&s1))
	c.Assert(err, IsNil)
	c.Assert(s1, Equals, "test_multi_prepare_tx1")

	var s2 string
	_, err = stmt2.QueryOne(pg.Scan(&s2))
	c.Assert(err, IsNil)
	c.Assert(s2, Equals, "test_multi_prepare_tx2")

	c.Assert(tx.Rollback(), IsNil)
}

func (t *TxTest) TestCopyFromInTransaction(c *C) {
	data := "hello\t5\nworld\t5\nfoo\t3\nbar\t3\n"

	_, err := t.db.Exec("DROP TABLE IF EXISTS test_copy_from")
	c.Assert(err, IsNil)

	_, err = t.db.Exec("CREATE TABLE test_copy_from(word text, len int)")
	c.Assert(err, IsNil)

	tx1, err := t.db.Begin()
	c.Assert(err, IsNil)
	tx2, err := t.db.Begin()
	c.Assert(err, IsNil)

	r := strings.NewReader(data)
	res, err := tx1.CopyFrom(r, "COPY test_copy_from FROM STDIN")
	c.Assert(err, IsNil)
	c.Assert(res.RowsAffected(), Equals, 4)

	var count int
	_, err = tx1.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, IsNil)
	c.Assert(count, Equals, 4)

	_, err = tx2.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, IsNil)
	c.Assert(count, Equals, 0)

	c.Assert(tx1.Commit(), IsNil)

	_, err = tx2.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, IsNil)
	c.Assert(count, Equals, 4) // assuming READ COMMITTED

	c.Assert(tx2.Rollback(), IsNil)

	_, err = t.db.Exec("DROP TABLE IF EXISTS test_copy_from")
	c.Assert(err, IsNil)
}

func (t *TxTest) TestCopyFromInTransactionWithErrors(c *C) {
	// too many fields on second line
	data := "hello\t5\nworld\t5\t6\t8\t9\nfoo\t3\nbar\t3\n"

	_, err := t.db.Exec("DROP TABLE IF EXISTS test_copy_from")
	c.Assert(err, IsNil)

	_, err = t.db.Exec("CREATE TABLE test_copy_from(word text, len int)")
	c.Assert(err, IsNil)
	_, err = t.db.Exec("INSERT INTO test_copy_from VALUES ('xxx', 3)")
	c.Assert(err, IsNil)

	tx1, err := t.db.Begin()
	c.Assert(err, IsNil)
	tx2, err := t.db.Begin()
	c.Assert(err, IsNil)

	_, err = tx1.Exec("INSERT INTO test_copy_from VALUES ('yyy', 3)")
	c.Assert(err, IsNil)

	r := strings.NewReader(data)
	_, err = tx1.CopyFrom(r, "COPY test_copy_from FROM STDIN")
	c.Assert(err, Not(IsNil))

	var count int
	_, err = tx1.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, Not(IsNil)) // transaction has errors, cannot proceed

	_, err = tx2.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, IsNil)
	c.Assert(count, Equals, 1)

	c.Assert(tx1.Commit(), IsNil) // actually ROLLBACK happens here

	_, err = tx2.QueryOne(pg.Scan(&count), "SELECT COUNT(*) FROM test_copy_from")
	c.Assert(err, IsNil)
	c.Assert(count, Equals, 1) // other transaction was rolled back so it's not 2 and not 6

	c.Assert(tx2.Rollback(), IsNil)

	_, err = t.db.Exec("DROP TABLE IF EXISTS test_copy_from")
	c.Assert(err, IsNil)
}
