package op

import (
	"crypto/rand"
	"crypto/rsa"
	"fmt"

	"git.coopgo.io/coopgo-platform/mobility-accounts/handlers"
	"git.coopgo.io/coopgo-platform/mobility-accounts/storage"
	"github.com/mitchellh/mapstructure"
	"github.com/ory/fosite"
	"github.com/spf13/viper"
)

type OIDCConfig struct {
	Enable     bool
	CSRFKey    bool `mapstructure:"csrf_key"`
	Port       bool
	Namespaces map[string]OIDCNamespaceConfig
}

type OIDCNamespaceConfig struct {
	Namespace    string
	SecretKey    string            `mapstructure:"secret_key"`
	TemplatesDir string            `mapstructure:"templates_dir"`
	MatchClaims  map[string]string `mapstructure:"match_claims"`
	Clients      []OIDCClient
}

type OIDCClient struct {
	ID            string
	OIDC          bool
	Secret        string
	RedirectURIs  []string `mapstructure:"redirect_uris"`
	ResponseTypes []string `mapstructure:"response_types"`
	GrantTypes    []string `mapstructure:"grant_types"`
	Scopes        []string
	Audience      []string
	Public        bool
	//OIDC specific
	TokenEndpointAuthMethod string `mapstructure:"token_endpoint_auth_method"`
}

type OIDCHandler struct {
	NamespaceProviders map[string]fosite.OAuth2Provider
	config             OIDCConfig
	handler            handlers.MobilityAccountsHandler
	Protocol           string //HTTP (dev env) or HTTPS
	PrivateKey         *rsa.PrivateKey
}

func NewOIDCHandler(h handlers.MobilityAccountsHandler, storage storage.Storage, config *viper.Viper) *OIDCHandler {
	var oidc_config OIDCConfig

	mapstructure.Decode(config.Get("services.oidc_provider").(map[string]any), &oidc_config)

	providers := map[string]fosite.OAuth2Provider{}

	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		return nil
	}

	for _, c := range oidc_config.Namespaces {
		np := NewProvider(c, h, storage, privateKey)

		providers[c.Namespace] = np
	}

	protocol := "https"
	if config.GetBool("dev_env") {
		protocol = "http"
	}

	return &OIDCHandler{
		config:             oidc_config,
		handler:            h,
		NamespaceProviders: providers,
		Protocol:           protocol,
		PrivateKey:         privateKey,
	}
}

func Run(done chan error, cfg *viper.Viper, handler handlers.MobilityAccountsHandler, storage storage.Storage) {
	var (
		address = "0.0.0.0:" + cfg.GetString("services.oidc_provider.port")
	)

	fmt.Println("-> OIDC provider endpoints on", address)

	s := NewOIDCHandler(handler, storage, cfg)

	err := NewOIDCServer(s, cfg)

	if err != nil {
		fmt.Println("OIDC server ended")
	}

	done <- err
}