package cmd

import (
	"github.com/go-errors/errors"
	"github.com/spf13/cobra"

	"github.com/supabase/cli/internal/sso/create"
	"github.com/supabase/cli/internal/sso/get"
	"github.com/supabase/cli/internal/sso/info"
	"github.com/supabase/cli/internal/sso/list"
	"github.com/supabase/cli/internal/sso/remove"
	"github.com/supabase/cli/internal/sso/update"
	"github.com/supabase/cli/internal/utils"
	"github.com/supabase/cli/internal/utils/flags"
)

var (
	ssoCmd = &cobra.Command{
		GroupID: groupManagementAPI,
		Use:     "sso",
		Short:   "Manage Single Sign-On (SSO) authentication for projects",
	}

	ssoProviderType = utils.EnumFlag{
		Allowed: []string{"saml"},
		// intentionally no default value so users have to specify --type saml explicitly
	}
	ssoMetadataFile         string
	ssoMetadataURL          string
	ssoSkipURLValidation    bool
	ssoMetadata             bool
	ssoAttributeMappingFile string
	ssoDomains              []string
	ssoAddDomains           []string
	ssoRemoveDomains        []string

	ssoAddCmd = &cobra.Command{
		Use:     "add",
		Short:   "Add a new SSO identity provider",
		Long:    "Add and configure a new connection to a SSO identity provider to your Supabase project.",
		Example: `  supabase sso add --type saml --project-ref mwjylndxudmiehsxhmmz --metadata-url 'https://...' --domains example.com`,
		RunE: func(cmd *cobra.Command, args []string) error {
			return create.Run(cmd.Context(), create.RunParams{
				ProjectRef:        flags.ProjectRef,
				Type:              ssoProviderType.String(),
				Format:            utils.OutputFormat.Value,
				MetadataFile:      ssoMetadataFile,
				MetadataURL:       ssoMetadataURL,
				SkipURLValidation: ssoSkipURLValidation,
				AttributeMapping:  ssoAttributeMappingFile,
				Domains:           ssoDomains,
			})
		},
	}

	ssoRemoveCmd = &cobra.Command{
		Use:     "remove <provider-id>",
		Short:   "Remove an existing SSO identity provider",
		Long:    "Remove a connection to an already added SSO identity provider. Removing the provider will prevent existing users from logging in. Please treat this command with care.",
		Args:    cobra.ExactArgs(1),
		Example: `  supabase sso remove b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz`,
		RunE: func(cmd *cobra.Command, args []string) error {
			if !utils.UUIDPattern.MatchString(args[0]) {
				return errors.Errorf("identity provider ID %q is not a UUID", args[0])
			}

			return remove.Run(cmd.Context(), flags.ProjectRef, args[0], utils.OutputFormat.Value)
		},
	}

	ssoUpdateCmd = &cobra.Command{
		Use:     "update <provider-id>",
		Short:   "Update information about an SSO identity provider",
		Long:    "Update the configuration settings of a already added SSO identity provider.",
		Args:    cobra.ExactArgs(1),
		Example: `  supabase sso update b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz --add-domains example.com`,
		RunE: func(cmd *cobra.Command, args []string) error {
			if !utils.UUIDPattern.MatchString(args[0]) {
				return errors.Errorf("identity provider ID %q is not a UUID", args[0])
			}

			return update.Run(cmd.Context(), update.RunParams{
				ProjectRef: flags.ProjectRef,
				ProviderID: args[0],
				Format:     utils.OutputFormat.Value,

				MetadataFile:      ssoMetadataFile,
				MetadataURL:       ssoMetadataURL,
				SkipURLValidation: ssoSkipURLValidation,
				AttributeMapping:  ssoAttributeMappingFile,
				Domains:           ssoDomains,
				AddDomains:        ssoAddDomains,
				RemoveDomains:     ssoRemoveDomains,
			})
		},
	}

	ssoShowCmd = &cobra.Command{
		Use:     "show <provider-id>",
		Short:   "Show information about an SSO identity provider",
		Long:    "Provides the information about an established connection to an identity provider. You can use --metadata to obtain the raw SAML 2.0 Metadata XML document stored in your project's configuration.",
		Args:    cobra.ExactArgs(1),
		Example: `  supabase sso show b5ae62f9-ef1d-4f11-a02b-731c8bbb11e8 --project-ref mwjylndxudmiehsxhmmz`,
		RunE: func(cmd *cobra.Command, args []string) error {
			if !utils.UUIDPattern.MatchString(args[0]) {
				return errors.Errorf("identity provider ID %q is not a UUID", args[0])
			}

			format := utils.OutputFormat.Value
			if ssoMetadata {
				format = utils.OutputMetadata
			}

			return get.Run(cmd.Context(), flags.ProjectRef, args[0], format)
		},
	}

	ssoListCmd = &cobra.Command{
		Use:     "list",
		Short:   "List all SSO identity providers for a project",
		Long:    "List all connections to a SSO identity provider to your Supabase project.",
		Example: `  supabase sso list --project-ref mwjylndxudmiehsxhmmz`,
		RunE: func(cmd *cobra.Command, args []string) error {
			return list.Run(cmd.Context(), flags.ProjectRef, utils.OutputFormat.Value)
		},
	}

	ssoInfoCmd = &cobra.Command{
		Use:     "info",
		Short:   "Returns the SAML SSO settings required for the identity provider",
		Long:    "Returns all of the important SSO information necessary for your project to be registered with a SAML 2.0 compatible identity provider.",
		Example: `  supabase sso info --project-ref mwjylndxudmiehsxhmmz`,
		RunE: func(cmd *cobra.Command, args []string) error {
			return info.Run(cmd.Context(), flags.ProjectRef, utils.OutputFormat.Value)
		},
	}
)

func init() {
	persistentFlags := ssoCmd.PersistentFlags()
	persistentFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
	ssoAddFlags := ssoAddCmd.Flags()
	ssoAddFlags.VarP(&ssoProviderType, "type", "t", "Type of identity provider (according to supported protocol).")
	ssoAddFlags.StringSliceVar(&ssoDomains, "domains", nil, "Comma separated list of email domains to associate with the added identity provider.")
	ssoAddFlags.StringVar(&ssoMetadataFile, "metadata-file", "", "File containing a SAML 2.0 Metadata XML document describing the identity provider.")
	ssoAddFlags.StringVar(&ssoMetadataURL, "metadata-url", "", "URL pointing to a SAML 2.0 Metadata XML document describing the identity provider.")
	ssoAddFlags.BoolVar(&ssoSkipURLValidation, "skip-url-validation", false, "Whether local validation of the SAML 2.0 Metadata URL should not be performed.")
	ssoAddFlags.StringVar(&ssoAttributeMappingFile, "attribute-mapping-file", "", "File containing a JSON mapping between SAML attributes to custom JWT claims.")
	ssoAddCmd.MarkFlagsMutuallyExclusive("metadata-file", "metadata-url")
	cobra.CheckErr(ssoAddCmd.MarkFlagRequired("type"))
	cobra.CheckErr(ssoAddCmd.MarkFlagFilename("metadata-file", "xml"))
	cobra.CheckErr(ssoAddCmd.MarkFlagFilename("attribute-mapping-file", "json"))

	ssoUpdateFlags := ssoUpdateCmd.Flags()
	ssoUpdateFlags.StringSliceVar(&ssoDomains, "domains", []string{}, "Replace domains with this comma separated list of email domains.")
	ssoUpdateFlags.StringSliceVar(&ssoAddDomains, "add-domains", []string{}, "Add this comma separated list of email domains to the identity provider.")
	ssoUpdateFlags.StringSliceVar(&ssoRemoveDomains, "remove-domains", []string{}, "Remove this comma separated list of email domains from the identity provider.")
	ssoUpdateFlags.StringVar(&ssoMetadataFile, "metadata-file", "", "File containing a SAML 2.0 Metadata XML document describing the identity provider.")
	ssoUpdateFlags.StringVar(&ssoMetadataURL, "metadata-url", "", "URL pointing to a SAML 2.0 Metadata XML document describing the identity provider.")
	ssoUpdateFlags.BoolVar(&ssoSkipURLValidation, "skip-url-validation", false, "Whether local validation of the SAML 2.0 Metadata URL should not be performed.")
	ssoUpdateFlags.StringVar(&ssoAttributeMappingFile, "attribute-mapping-file", "", "File containing a JSON mapping between SAML attributes to custom JWT claims.")
	ssoUpdateCmd.MarkFlagsMutuallyExclusive("metadata-file", "metadata-url")
	ssoUpdateCmd.MarkFlagsMutuallyExclusive("domains", "add-domains")
	ssoUpdateCmd.MarkFlagsMutuallyExclusive("domains", "remove-domains")
	cobra.CheckErr(ssoUpdateCmd.MarkFlagFilename("metadata-file", "xml"))
	cobra.CheckErr(ssoUpdateCmd.MarkFlagFilename("attribute-mapping-file", "json"))

	ssoShowFlags := ssoShowCmd.Flags()
	ssoShowFlags.BoolVar(&ssoMetadata, "metadata", false, "Show SAML 2.0 XML Metadata only")

	ssoCmd.AddCommand(ssoAddCmd)
	ssoCmd.AddCommand(ssoRemoveCmd)
	ssoCmd.AddCommand(ssoUpdateCmd)
	ssoCmd.AddCommand(ssoShowCmd)
	ssoCmd.AddCommand(ssoListCmd)
	ssoCmd.AddCommand(ssoInfoCmd)

	rootCmd.AddCommand(ssoCmd)
}
