package auth

import (
	"code.secondbit.org/uuid.hg"
	"github.com/lib/pq"
	"github.com/secondbit/pan"
)

func (ac AuthorizationCode) GetSQLTableName() string {
	return "authorization_codes"
}

func (p *postgres) getAuthorizationCodeSQL(code string) *pan.Query {
	var ac AuthorizationCode
	fields, _ := pan.GetFields(ac)
	query := pan.New(pan.POSTGRES, "SELECT "+pan.QueryList(fields)+" FROM "+pan.GetTableName(ac))
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(ac, "Code")+" = ?", code)
	return query.FlushExpressions(" ")
}

func (p *postgres) getAuthorizationCode(code string) (AuthorizationCode, error) {
	query := p.getAuthorizationCodeSQL(code)
	rows, err := p.db.Query(query.String(), query.Args...)
	if err != nil {
		return AuthorizationCode{}, err
	}
	var ac AuthorizationCode
	var found bool
	for rows.Next() {
		err := pan.Unmarshal(rows, &ac)
		if err != nil {
			return ac, err
		}
		found = true
	}
	if err = rows.Err(); err != nil {
		return ac, err
	}
	if !found {
		return ac, ErrAuthorizationCodeNotFound
	}
	return ac, nil
}

func (p *postgres) saveAuthorizationCodeSQL(authCode AuthorizationCode) *pan.Query {
	fields, values := pan.GetFields(authCode)
	query := pan.New(pan.POSTGRES, "INSERT INTO "+pan.GetTableName(authCode))
	query.Include("(" + pan.QueryList(fields) + ")")
	query.Include("VALUES")
	query.Include("("+pan.VariableList(len(values))+")", values...)
	return query.FlushExpressions(" ")
}

func (p *postgres) saveAuthorizationCode(authCode AuthorizationCode) error {
	query := p.saveAuthorizationCodeSQL(authCode)
	_, err := p.db.Exec(query.String(), query.Args...)
	if e, ok := err.(*pq.Error); ok && e.Constraint == "authorization_codes_pkey" {
		err = ErrAuthorizationCodeAlreadyExists
	}
	return err
}

func (p *postgres) deleteAuthorizationCodeSQL(code string) *pan.Query {
	var authCode AuthorizationCode
	query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(authCode))
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(authCode, "Code")+" = ?", code)
	return query.FlushExpressions(" ")
}

func (p *postgres) deleteAuthorizationCode(code string) error {
	query := p.deleteAuthorizationCodeSQL(code)
	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 == 0 {
		return ErrAuthorizationCodeNotFound
	}
	return nil
}

func (p *postgres) deleteAuthorizationCodesByProfileIDSQL(profileID uuid.ID) *pan.Query {
	var authCode AuthorizationCode
	query := pan.New(pan.POSTGRES, "DELETE FROM "+pan.GetTableName(authCode))
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(authCode, "ProfileID")+" = ?", profileID)
	return query.FlushExpressions(" ")
}

func (p *postgres) deleteAuthorizationCodesByProfileID(profileID uuid.ID) error {
	query := p.deleteAuthorizationCodesByProfileIDSQL(profileID)
	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 == 0 {
		return ErrProfileNotFound
	}
	return nil
}

func (p *postgres) useAuthorizationCodeSQL(code string) *pan.Query {
	var authCode AuthorizationCode
	query := pan.New(pan.POSTGRES, "UPDATE "+pan.GetTableName(authCode)+" SET ")
	query.Include(pan.GetUnquotedColumn(authCode, "Used")+" = ?", true)
	query.IncludeWhere()
	query.Include(pan.GetUnquotedColumn(authCode, "Code")+" = ?", code)
	return query.FlushExpressions(" ")
}

func (p *postgres) useAuthorizationCode(code string) error {
	query := p.useAuthorizationCodeSQL(code)
	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 == 0 {
		return ErrAuthorizationCodeNotFound
	}
	return nil
}
