package csp

import (
	"crypto/rand"
	"encoding/base64"
	"log"
	"net/http"
)

type cspKeyword uint

//go:generate stringer -type=cspKeyword -linecomment
const (
	Self                   cspKeyword = iota // self
	TrustedTypesEval                         // trusted-types-eval
	InlineSpeculationRules                   // inline-speculation-rules
	StrictDynamic                            // strict-dynamic
	ReportSample                             // report-sample
)

type cspDirective struct {
	values   []string
	keywords map[cspKeyword]struct{}
}

type Builder struct {
	directives map[string]*cspDirective
	nonce      string
	request    *http.Request
}

func (csp *Builder) getDirective(directive string) *cspDirective {
	d, ok := csp.directives[directive]
	if !ok {
		d = &cspDirective{
			values:   []string{},
			keywords: map[cspKeyword]struct{}{},
		}
		csp.directives[directive] = d
	}
	return d
}

// WithValue adds a directive to the policy.
func (csp *Builder) WithValue(directive string, values ...string) *Builder {
	csp.getDirective(directive).withValue(values...)
	return csp
}

func (d *cspDirective) withValue(values ...string) *cspDirective {
	d.values = append(d.values, values...)
	return d
}

// WithKeyword adds a directive to the policy.
func (csp *Builder) WithKeyword(directive string, keyword cspKeyword) *Builder {
	csp.getDirective(directive).keywords[keyword] = struct{}{}
	return csp
}

// WithNonce adds a `nonce-…` value to the directive. The actual nonce value is
// generated when the policy is built.
func (csp *Builder) WithNonce(directive string) *Builder {
	if csp.nonce != "" {
		var b [16]byte
		if _, err := rand.Read(b[:]); err != nil {
			log.Fatalf("Failed to generate nonce: %v", err)
		}
		csp.nonce = base64.RawURLEncoding.EncodeToString(b[:])
	}
	return csp.WithValue(directive, "nonce-"+csp.nonce)
}

func (csp *Builder) Build() string {
	panic("TODO")
}
