package store

import (
	"context"
	"database/sql"
	"fmt"

	"code.justin.tv/d8a/buddy/lib/config"
)

//EnsureUserPair should only be run after a successful run of clusters.EnsureChildUsers
//This code assumes EnsureChildUsers was successful.  It then does the following:
// - Verifies that user rows exist in buddystore for <role>_01 and <role>_02
// - Verifies that a pair row exists in buddystore (creates it if not)
// - Updates the user rows to point at the pair row
func EnsureUserPair(storeDb *sql.DB, cluster *config.Cluster, roleName string) error {

	clusterName := cluster.Name
	leftUsername := fmt.Sprintf("%s_01", roleName)
	rightUsername := fmt.Sprintf("%s_02", roleName)

	leftUser := cluster.GetUser(leftUsername)
	if leftUser == nil {
		return fmt.Errorf("could not find user %s in cluster %s", leftUsername, cluster.Name)
	}
	rightUser := cluster.GetUser(rightUsername)
	if rightUser == nil {
		return fmt.Errorf("could not find user %s in cluster %s", leftUsername, cluster.Name)
	}

	rolePair := cluster.GetRolePair(roleName)
	if rolePair == nil {
		err := InsertRolePair(storeDb, cluster, &config.RolePair{
			Name: roleName,
		})
		if err != nil {
			return err
		}

		clusterList, err := GetClusters(storeDb)
		if err != nil {
			return err
		}
		cluster = clusterList.GetCluster(clusterName)
		if cluster == nil {
			return fmt.Errorf("could not find cluster %s", clusterName)
		}
		rolePair = cluster.GetRolePair(roleName)
		if rolePair == nil {
			return fmt.Errorf("creating user pair %s in cluster %s didn't work for some reason", roleName, clusterName)
		}
	}

	return SetRolePair(storeDb, context.Background(), rolePair, leftUser, rightUser)
}

type dbRolePair struct {
	id        int
	name      string
	clusterId int
}

func readUserPairs(db *sql.DB) ([]*dbRolePair, error) {
	var clusterPairs []*dbRolePair
	rows, err := db.Query("SELECT id, name, cluster_id FROM role_pairs")
	if err != nil {
		return clusterPairs, err
	}

	defer TryClose(rows)

	for rows.Next() {
		user, err := readUserPair(rows)
		if err != nil {
			return clusterPairs, err
		}
		clusterPairs = append(clusterPairs, user)
	}

	return clusterPairs, nil
}

func readUserPair(rows *sql.Rows) (*dbRolePair, error) {
	var id, clusterId int
	var name string
	err := rows.Scan(&id, &name, &clusterId)
	if err != nil {
		return nil, err
	}

	return &dbRolePair{
		id:        id,
		name:      name,
		clusterId: clusterId,
	}, nil
}

func InsertRolePair(db *sql.DB, cluster *config.Cluster, pair *config.RolePair) error {
	_, err := db.Exec("INSERT INTO role_pairs (name, cluster_id) VALUES ($1, $2)", pair.Name, cluster.Id)
	return err
}

func UpdateRolePair(db *sql.DB, pair *config.RolePair) error {
	_, err := db.Exec("UPDATE role_pairs SET name=$1 WHERE id=$2", pair.Name, pair.Id)
	return err
}

func DeleteRolePair(db *sql.DB, pair *config.RolePair) error {
	_, err := db.Exec("DELETE FROM role_pairs WHERE id=$1", pair.Id)
	return err
}

func SetRolePair(db *sql.DB, context context.Context, rolePair *config.RolePair, leftUser *config.User, rightUser *config.User) (err error) {
	var tx *sql.Tx
	tx, err = db.BeginTx(context, nil)
	if err != nil {
		return
	}

	defer func() {
		if err != nil {
			fmt.Println(tx.Rollback())
		} else {
			err = tx.Commit()
		}
	}()

	_, err = db.Exec("UPDATE users SET role_pair_id=NULL WHERE role_pair_id=$1", rolePair.Id)
	if err != nil {
		return
	}

	_, err = db.Exec("UPDATE users SET role_pair_id=$1 WHERE id=$2 OR id=$3", rolePair.Id, leftUser.Id, rightUser.Id)
	return
}
