package transfercoderepo

import (
	"database/sql"
	"errors"

	"github.com/jmoiron/sqlx"
	log "github.com/sirupsen/logrus"

	"github.com/zachmann/mytoken/internal/config"
	"github.com/zachmann/mytoken/internal/db"
	"github.com/zachmann/mytoken/internal/model"
)

// TransferCode is a type used to transfer a token
type TransferCode struct {
	proxyToken
	Attributes transferCodeAttributes
}

type transferCodeAttributes struct {
	NewST db.BitBool
	model.ResponseType
}

// NewTransferCode creates a new TransferCode for the passed jwt
func NewTransferCode(jwt string, newST bool, responseType model.ResponseType) (*TransferCode, error) {
	pt := newProxyToken(config.Get().Features.Polling.Len)
	if err := pt.SetJWT(jwt); err != nil {
		return nil, err
	}
	transferCode := &TransferCode{
		proxyToken: *pt,
		Attributes: transferCodeAttributes{
			NewST:        db.BitBool(newST),
			ResponseType: responseType,
		},
	}
	return transferCode, nil
}

// ParseTransferCode creates a new transfer code from a transfer code string
func ParseTransferCode(token string) *TransferCode {
	return &TransferCode{proxyToken: *parseProxyToken(token)}
}

// CreatePollingCode creates a polling code
func CreatePollingCode(pollingCode string, responseType model.ResponseType) *TransferCode {
	pt := createProxyToken(pollingCode)
	return &TransferCode{
		proxyToken: *pt,
		Attributes: transferCodeAttributes{
			NewST:        true,
			ResponseType: responseType,
		},
	}
}

// Store stores the TransferCode in the database
func (tc TransferCode) Store(tx *sqlx.Tx) error {
	log.Debug("Storing transfer code")
	return db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error {
		if err := tc.proxyToken.Store(tx); err != nil {
			return err
		}
		_, err := tx.Exec(`INSERT INTO TransferCodesAttributes (id, expires_in,  revoke_ST, response_type) VALUES(?,?,?,?)`, tc.id, config.Get().Features.Polling.PollingCodeExpiresAfter, tc.Attributes.NewST, tc.Attributes.ResponseType)
		return err
	})
}

// GetRevokeJWT returns a bool indicating if the linked jwt should also be revoked when this TransferCode is revoked or not
func (tc TransferCode) GetRevokeJWT(tx *sqlx.Tx) (bool, error) {
	var revokeST db.BitBool
	err := db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error {
		if err := tx.Get(&revokeST, `SELECT revoke_ST FROM TransferCodesAttributes WHERE id=?`, tc.id); err != nil {
			if errors.Is(err, sql.ErrNoRows) {
				return nil
			}
			return err
		}
		return nil
	})
	return bool(revokeST), err
}