package service

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"testing"

	"github.com/go-jose/go-jose/v4"
	josejwt "github.com/go-jose/go-jose/v4/jwt"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestBackchannelLogoutService_BuildLogoutToken(t *testing.T) {
	t.Setenv("BACKCHANNEL_LOGOUT_ISSUER", "https://sso.example.com/oidc")

	svc, err := NewBackchannelLogoutService()
	require.NoError(t, err)

	token, err := svc.BuildLogoutToken("client-1", "user-1", "sid-1")
	require.NoError(t, err)
	require.NotEmpty(t, token)

	jwksRaw, err := svc.MarshalPublicJWKS()
	require.NoError(t, err)

	var jwks struct {
		Keys []jose.JSONWebKey `json:"keys"`
	}
	require.NoError(t, json.Unmarshal(jwksRaw, &jwks))
	require.Len(t, jwks.Keys, 1)

	parsed, err := josejwt.ParseSigned(token, []jose.SignatureAlgorithm{jose.RS256})
	require.NoError(t, err)

	var claims struct {
		Issuer  string         `json:"iss"`
		Subject string         `json:"sub"`
		Aud     any            `json:"aud"`
		Iat     int64          `json:"iat"`
		Jti     string         `json:"jti"`
		Sid     string         `json:"sid"`
		Events  map[string]any `json:"events"`
	}
	require.NoError(t, parsed.Claims(jwks.Keys[0].Key, &claims))

	assert.Equal(t, "https://sso.example.com/oidc", claims.Issuer)
	assert.Equal(t, "user-1", claims.Subject)
	switch aud := claims.Aud.(type) {
	case string:
		assert.Equal(t, "client-1", aud)
	case []any:
		assert.Len(t, aud, 1)
		assert.Equal(t, "client-1", aud[0])
	default:
		t.Fatalf("unexpected aud type: %T", claims.Aud)
	}
	assert.NotZero(t, claims.Iat)
	assert.NotEmpty(t, claims.Jti)
	assert.Equal(t, "sid-1", claims.Sid)
	_, ok := claims.Events[backchannelLogoutEventURI]
	assert.True(t, ok)
}

func TestBackchannelLogoutService_SendLogoutToken(t *testing.T) {
	var body string
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, http.MethodPost, r.Method)
		assert.Equal(t, "application/x-www-form-urlencoded", r.Header.Get("Content-Type"))
		raw, _ := io.ReadAll(r.Body)
		body = string(raw)
		w.WriteHeader(http.StatusNoContent)
	})

	svc, err := NewBackchannelLogoutService()
	require.NoError(t, err)
	svc.HTTPClient = clientForHandler(handler)

	statusCode, err := svc.SendLogoutToken(context.Background(), "https://rp.example.com/backchannel-logout", "signed-token")
	require.NoError(t, err)
	assert.Equal(t, http.StatusNoContent, statusCode)
	assert.Equal(t, "logout_token=signed-token", body)
}
