package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/mem9-ai/drive9/pkg/innerauth"
	"github.com/mem9-ai/drive9/pkg/tenant"
)

const (
	envTiDBCloudIAMAPIMode     = "DRIVE9_TIDBCLOUD_IAM_API_MODE"
	envTiDBCloudBillingAPIMode = "DRIVE9_TIDBCLOUD_BILLING_API_MODE"
	envTiDBCloudInnerAPIToken  = "DRIVE9_TIDBCLOUD_INNER_API_TOKEN"
	envTiDBCloudDataAPIURL     = "DRIVE9_TIDBCLOUD_DATA_API_URL"
	envTiDBCloudDataAPIToken   = "DRIVE9_TIDBCLOUD_DATA_API_TOKEN"
	apiModeOpenAPI             = "openapi"
	apiModeInner               = "inner"
)

type tiDBCloudAPIModes struct {
	iam     string
	billing string
}

func tiDBCloudAPIModesFromEnv(provider string) (tiDBCloudAPIModes, error) {
	iam, err := parseTiDBCloudAPIMode(envTiDBCloudIAMAPIMode)
	if err != nil {
		return tiDBCloudAPIModes{}, err
	}
	billing, err := parseTiDBCloudAPIMode(envTiDBCloudBillingAPIMode)
	if err != nil {
		return tiDBCloudAPIModes{}, err
	}
	if provider == tenant.ProviderTiDBCloudNative {
		switch {
		case iam == apiModeInner:
			return tiDBCloudAPIModes{}, fmt.Errorf("%s=%q is not supported with %s", envTiDBCloudIAMAPIMode, iam, provider)
		case billing == apiModeInner:
			return tiDBCloudAPIModes{}, fmt.Errorf("%s=%q is not supported with %s", envTiDBCloudBillingAPIMode, billing, provider)
		}
	} else if iam != billing {
		return tiDBCloudAPIModes{}, fmt.Errorf("%s=%q and %s=%q must use the same mode for %s", envTiDBCloudIAMAPIMode, iam, envTiDBCloudBillingAPIMode, billing, provider)
	}
	if (iam == apiModeInner || billing == apiModeInner) && strings.TrimSpace(os.Getenv(envTiDBCloudInnerAPIToken)) == "" {
		return tiDBCloudAPIModes{}, fmt.Errorf("%s is required when IAM or Billing API mode is inner", envTiDBCloudInnerAPIToken)
	}
	return tiDBCloudAPIModes{iam: iam, billing: billing}, nil
}

func parseTiDBCloudAPIMode(envName string) (string, error) {
	mode := strings.ToLower(strings.TrimSpace(os.Getenv(envName)))
	if mode == "" {
		return apiModeOpenAPI, nil
	}
	if mode != apiModeOpenAPI && mode != apiModeInner {
		return "", fmt.Errorf("invalid %s: must be %q or %q", envName, apiModeOpenAPI, apiModeInner)
	}
	return mode, nil
}

func innerClientFromEnv(mode, urlEnv string) (*innerauth.Client, error) {
	if mode != apiModeInner {
		return nil, nil
	}
	baseURL := strings.TrimSpace(os.Getenv(urlEnv))
	if baseURL == "" {
		return nil, fmt.Errorf("%s is required when its API mode is inner", urlEnv)
	}
	return innerauth.NewClient(innerauth.ClientConfig{
		BaseURL: baseURL,
		Token:   strings.TrimSpace(os.Getenv(envTiDBCloudInnerAPIToken)),
	})
}

// innerDataClientFromEnv builds the dedicated data-plane inner client used to
// validate the server-owned shared TiDB Cloud credential. Unlike the
// control-plane IAM/Billing inner clients, the data-plane client is enabled by
// the presence of DRIVE9_TIDBCLOUD_DATA_API_URL alone — it does not follow the
// IAM/Billing API modes, because the shared credential is only ever verified
// internally and may live at a different address than the caller-facing
// control-plane endpoints.
func innerDataClientFromEnv(provider string) (*innerauth.Client, error) {
	baseURL := strings.TrimSpace(os.Getenv(envTiDBCloudDataAPIURL))
	token := strings.TrimSpace(os.Getenv(envTiDBCloudDataAPIToken))
	if baseURL == "" {
		if token != "" {
			return nil, fmt.Errorf("%s is required when %s is set", envTiDBCloudDataAPIURL, envTiDBCloudDataAPIToken)
		}
		return nil, nil
	}
	if provider != tenant.ProviderTiDBCloudNativeShared {
		return nil, fmt.Errorf("%s is only supported with %s", envTiDBCloudDataAPIURL, tenant.ProviderTiDBCloudNativeShared)
	}
	if token == "" {
		return nil, fmt.Errorf("%s is required when %s is set", envTiDBCloudDataAPIToken, envTiDBCloudDataAPIURL)
	}
	client, err := innerauth.NewClient(innerauth.ClientConfig{
		BaseURL: baseURL,
		Token:   token,
	})
	if err != nil {
		return nil, fmt.Errorf("%s: %w", envTiDBCloudDataAPIURL, err)
	}
	return client, nil
}

// sharedAccessProviders selects the account/billing providers used for the
// startup shared-credential validation. A configured data-plane client takes
// over BOTH slots — the server-owned shared credential is verified against
// the dedicated data-plane endpoint, never split across endpoints.
func sharedAccessProviders(iam, billing, data *innerauth.Client) (innerauth.AccountProvider, innerauth.BillingPlanProvider) {
	if data != nil {
		return data, data
	}
	return innerAccountProvider(iam), innerBillingPlanProvider(billing)
}

func innerAccountProvider(client *innerauth.Client) innerauth.AccountProvider {
	if client == nil {
		return nil
	}
	return client
}

func innerBillingPlanProvider(client *innerauth.Client) innerauth.BillingPlanProvider {
	if client == nil {
		return nil
	}
	return client
}
