package zitadel_test

import (
	"context"
	"crypto/rand"
	"crypto/rsa"
	"encoding/base64"
	"encoding/json"
	"math/big"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"gateway/internal/library/zitadel"

	"github.com/golang-jwt/jwt/v4"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	testPAT       = "pat"
	testClientID  = "gw-client"
	testSecret    = "gw-secret"
	testIssuerURL = "https://zitadel.example.com"
)

type jwksFixture struct {
	Server *httptest.Server
	Client *zitadel.Client
	Key    *rsa.PrivateKey
	KID    string
	Issuer string
}

func newJWKSFixture(t *testing.T) *jwksFixture {
	t.Helper()

	key, err := rsa.GenerateKey(rand.Reader, 2048)
	require.NoError(t, err)

	kid := "test-kid"
	jwks := map[string]any{
		"keys": []map[string]any{{
			"kty": "RSA",
			"kid": kid,
			"n":   base64.RawURLEncoding.EncodeToString(key.N.Bytes()),
			"e":   base64.RawURLEncoding.EncodeToString(big.NewInt(int64(key.PublicKey.E)).Bytes()),
		}},
	}

	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		assert.Equal(t, "/oauth/v2/keys", r.URL.Path)
		w.Header().Set("Content-Type", "application/json")
		assert.NoError(t, json.NewEncoder(w).Encode(jwks))
	}))

	client, err := zitadel.NewClient(zitadel.Conf{
		Issuer:            srv.URL,
		ServiceUserToken:  testPAT,
		OAuthClientID:     testClientID,
		OAuthClientSecret: "secret",
	})
	require.NoError(t, err)

	t.Cleanup(srv.Close)

	return &jwksFixture{
		Server: srv,
		Client: client,
		Key:    key,
		KID:    kid,
		Issuer: srv.URL,
	}
}

func (f *jwksFixture) signIDToken(t *testing.T, claims jwt.MapClaims) string {
	t.Helper()

	token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
	token.Header["kid"] = f.KID
	raw, err := token.SignedString(f.Key)
	require.NoError(t, err)
	return raw
}

func (f *jwksFixture) validClaims(now time.Time) jwt.MapClaims {
	return jwt.MapClaims{
		"iss":            f.Issuer,
		"sub":            "zitadel-sub-1",
		"aud":            testClientID,
		"exp":            now.Add(time.Hour).Unix(),
		"email":          "user@example.com",
		"email_verified": true,
	}
}

func (f *jwksFixture) verify(t *testing.T, raw string) *zitadel.IDTokenClaims {
	t.Helper()

	claims, err := f.Client.VerifyIDToken(context.Background(), raw)
	require.NoError(t, err)
	return claims
}
