package wsproxy

import (
	"context"
	"net/http"
	"net/url"

	"cdr.dev/slog"

	"github.com/coder/coder/v2/coderd/workspaceapps"
	"github.com/coder/coder/v2/enterprise/wsproxy/wsproxysdk"
)

var _ workspaceapps.SignedTokenProvider = (*TokenProvider)(nil)

type TokenProvider struct {
	DashboardURL *url.URL
	AccessURL    *url.URL
	AppHostname  string

	Client      *wsproxysdk.Client
	SecurityKey workspaceapps.SecurityKey
	Logger      slog.Logger
}

func (p *TokenProvider) FromRequest(r *http.Request) (*workspaceapps.SignedToken, bool) {
	return workspaceapps.FromRequest(r, p.SecurityKey)
}

func (p *TokenProvider) Issue(ctx context.Context, rw http.ResponseWriter, r *http.Request, issueReq workspaceapps.IssueTokenRequest) (*workspaceapps.SignedToken, string, bool) {
	appReq := issueReq.AppRequest.Normalize()
	err := appReq.Validate()
	if err != nil {
		workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "invalid app request")
		return nil, "", false
	}
	issueReq.AppRequest = appReq

	resp, ok := p.Client.IssueSignedAppTokenHTML(ctx, rw, issueReq)
	if !ok {
		return nil, "", false
	}

	// Check that it verifies properly and matches the string.
	token, err := p.SecurityKey.VerifySignedToken(resp.SignedTokenStr)
	if err != nil {
		workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "failed to verify newly generated signed token")
		return nil, "", false
	}

	// Check that it matches the request.
	if !token.MatchesRequest(appReq) {
		workspaceapps.WriteWorkspaceApp500(p.Logger, p.DashboardURL, rw, r, &appReq, err, "newly generated signed token does not match request")
		return nil, "", false
	}

	return &token, resp.SignedTokenStr, true
}
