package applications

import (
	"encoding/json"
	"git.openprivacy.ca/cwtch.im/tapir"
	"git.openprivacy.ca/cwtch.im/tapir/primitives/privacypass"
	"git.openprivacy.ca/openprivacy/log"
)

// TokenApplication provides Tokens for PoW
type TokenApplication struct {
	TranscriptApp
	TokenService *privacypass.TokenServer
	Tokens       []*privacypass.Token
}

// HasTokensCapability is granted once the client has obtained signed tokens
const HasTokensCapability = tapir.Capability("HasTokensCapability")
const numTokens = 10

// NewInstance should always return a new instantiation of the application.
func (tokenapp *TokenApplication) NewInstance() tapir.Application {
	app := new(TokenApplication)
	app.TokenService = tokenapp.TokenService
	return app
}

// Init is run when the connection is first started.
func (tokenapp *TokenApplication) Init(connection tapir.Connection) {
	tokenapp.Transcript().NewProtocol("token-app")
	log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
	if connection.IsOutbound() {
		tokens, blinded := privacypass.GenerateBlindedTokenBatch(numTokens)
		data, _ := json.Marshal(blinded)
		connection.Send(data)
		var signedBatch privacypass.SignedBatchWithProof
		err := json.Unmarshal(connection.Expect(), &signedBatch)
		if err == nil {
			verified := privacypass.UnblindSignedTokenBatch(tokens, blinded, signedBatch.SignedTokens, tokenapp.TokenService.Y, signedBatch.Proof, tokenapp.Transcript())
			if verified {
				log.Debugf("Successfully obtained signed tokens")
				tokenapp.Tokens = tokens
				connection.SetCapability(HasTokensCapability)
				return
			}
			// This will close the connection by default and no tokens will be available.
			// This usecase can be checked by the existing WaitForCapabilityOrClose() function using the HasTokensCapability
			// If the connection closes without the HasTokensCapability then the error can be handled by whatever client needs it
			log.Debugf("Failed to verify signed token batch")
		}
		return
	}

	// We are the server
	var blinded []privacypass.BlindedToken
	err := json.Unmarshal(connection.Expect(), &blinded)
	if err == nil {
		batchProof, err := tokenapp.TokenService.SignBlindedTokenBatch(blinded, tokenapp.Transcript())
		if err != nil {
			return
		}
		log.Debugf(tokenapp.Transcript().OutputTranscriptToAudit())
		data, _ := json.Marshal(batchProof)
		connection.Send(data)
		return
	}
}
