package auth

import (
	"context"
	"database/sql"
	"net/http"
	"time"

	"github.com/golang-jwt/jwt/v5"

	"github.com/CMSgov/bcda-app/bcda/auth/client"
	"github.com/CMSgov/bcda-app/bcda/models/postgres"
	"github.com/CMSgov/bcda-app/log"
)

const (
	SSAS = "ssas"
)

func GetProviderName() string {
	return SSAS
}

func NewProvider(db *sql.DB) Provider {
	r := postgres.NewRepository(db)
	c, err := client.NewSSASClient()
	if err != nil {
		log.Auth.Errorf("no client for SSAS. no provider set; %s", err.Error())
	}

	return SSASPlugin{client: c, repository: r}
}

type AuthData struct {
	ACOID       string
	TokenID     string
	ClientID    string
	SystemID    string
	CMSID       string
	Blacklisted bool
}

type Credentials struct {
	ClientID     string    `json:"client_id"`
	ClientSecret string    `json:"client_secret"`
	ClientName   string    `json:"client_name"`
	SystemID     string    `json:"system_id"`
	Token        string    `json:"token"`
	ExpiresAt    time.Time `json:"expires_at"`
}

type CommonClaims struct {
	ClientID string   `json:"cid,omitempty"`
	SystemID string   `json:"sys,omitempty"`
	Data     string   `json:"dat,omitempty"`
	Scopes   []string `json:"scp,omitempty"`
	ACOID    string   `json:"aco,omitempty"`
	UUID     string   `json:"id,omitempty"`
	jwt.RegisteredClaims
}

// Provider defines operations performed through an authentication provider.
type Provider interface {
	// FindAndCreateACOCredentials takes an ACO ID and calls RegisterSystem, then formats the results
	FindAndCreateACOCredentials(ACOID string, IPs []string) (string, error)

	// RegisterSystem adds a software client for the ACO identified by localID.
	RegisterSystem(localID, publicKey, groupID string, ips ...string) (Credentials, error)

	// ResetSecret new or replace existing Credentials for the given clientID
	ResetSecret(clientID string) (Credentials, error)

	// RevokeSystemCredentials any existing Credentials for the given clientID
	RevokeSystemCredentials(clientID string) error

	// MakeAccessToken mints an access token for the given credentials
	MakeAccessToken(credentials Credentials, r *http.Request) (string, error)

	// RevokeAccessToken a specific access token identified in a base64 encoded token string
	RevokeAccessToken(tokenString string) error

	// VerifyToken decodes a base64 encoded token string into a structured token
	VerifyToken(ctx context.Context, tokenString string) (*jwt.Token, error)

	// GetVersion gets the version of the provider
	GetVersion() (string, error)

	//getAuthDataFromClaims gets AuthData from Token Claims
	getAuthDataFromClaims(*CommonClaims) (AuthData, error)
}
