package auth

import (
	"github.com/lib/pq"
	"github.com/secondbit/pan"
)

func (s Scope) GetSQLTableName() string {
	return "scopes"
}

func (p *postgres) createScopesSQL(scopes []Scope) *pan.Query {
	fields, _ := pan.GetFields(scopes[0])
	query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(scopes[0]))
	query.Include("(" + pan.QueryList(fields) + ")")
	query.Include("VALUES")
	query.FlushExpressions(" ")
	for _, scope := range scopes {
		_, values := pan.GetFields(scope)
		query.Include("("+pan.VariableList(len(values))+")", values...)
	}
	return query.FlushExpressions(", ")
}

func (p *postgres) createScopes(scopes []Scope) error {
	if len(scopes) < 1 {
		return nil
	}
	query := p.createScopesSQL(scopes)
	_, err := p.db.Exec(query.String(), query.Args...)
	if e, ok := err.(*pq.Error); ok && e.Constraint == "scopes_pkey" {
		err = ErrScopeAlreadyExists
	}
	return err
}

func (p *postgres) getScopesSQL(ids []string) *pan.Query {
	var scope Scope
	intids := make([]interface{}, len(ids))
	for pos, id := range ids {
		intids[pos] = id
	}
	fields, _ := pan.GetFields(scope)
	query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
	query.Include("("+pan.VariableList(len(ids))+")", intids...)
	return query.FlushExpressions(" ")
}

func (p *postgres) getScopes(ids []string) ([]Scope, error) {
	query := p.getScopesSQL(ids)
	rows, err := p.db.Query(query.String(), query.Args...)
	if err != nil {
		return []Scope{}, err
	}
	var scopes []Scope
	for rows.Next() {
		var scope Scope
		err := pan.Unmarshal(rows, &scope)
		if err != nil {
			return scopes, err
		}
		scopes = append(scopes, scope)
	}
	if err = rows.Err(); err != nil {
		return scopes, err
	}
	return scopes, nil
}

func (p *postgres) updateScopeSQL(id string, change ScopeChange) *pan.Query {
	var scope Scope
	query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(scope)+" SET ")
	query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Name")+" = ?", change.Name)
	query.IncludeIfNotNil(pan.GetUnquotedColumn(scope, "Description")+" = ?", change.Description)
	query.FlushExpressions(", ")
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(scope, "ID")+" = ?", id)
	return query.FlushExpressions(" ")
}

func (p *postgres) updateScope(id string, change ScopeChange) error {
	if change.Empty() {
		return nil
	}
	query := p.updateScopeSQL(id, change)
	res, err := p.db.Exec(query.String(), query.Args...)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows < 1 {
		return ErrScopeNotFound
	}
	return err
}

func (p *postgres) removeScopesSQL(ids []string) *pan.Query {
	var scope Scope
	intids := make([]interface{}, len(ids))
	for pos, id := range ids {
		intids[pos] = id
	}
	query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(scope))
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(scope, "ID") + " IN")
	query.Include("("+pan.VariableList(len(ids))+")", intids...)
	return query.FlushExpressions(" ")
}

func (p *postgres) removeScopes(ids []string) error {
	query := p.removeScopesSQL(ids)
	res, err := p.db.Exec(query.String(), query.Args...)
	if err != nil {
		return err
	}
	rows, err := res.RowsAffected()
	if err != nil {
		return err
	}
	if rows < 1 {
		return ErrScopeNotFound
	}
	return nil
}

func (p *postgres) listScopesSQL() *pan.Query {
	var scope Scope
	fields, _ := pan.GetFields(scope)
	query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(scope))
	return query.FlushExpressions(" ")
}

func (p *postgres) listScopes() ([]Scope, error) {
	query := p.listScopesSQL()
	rows, err := p.db.Query(query.String(), query.Args...)
	if err != nil {
		return []Scope{}, err
	}
	var scopes []Scope
	for rows.Next() {
		var scope Scope
		err = pan.Unmarshal(rows, &scope)
		if err != nil {
			return scopes, err
		}
		scopes = append(scopes, scope)
	}
	if err = rows.Err(); err != nil {
		return scopes, err
	}
	return scopes, nil
}
