package gitea

import (
	"fmt"

	"code.gitea.io/sdk/gitea"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

const (
	oauth2KeyName         string = "name"
	oauth2KeyRedirectURIs string = "redirect_uris"
	oauth2KeyClientId     string = "client_id"
	oauth2KeyClientSecret string = "client_secret"
)

func resourceGiteaOauthApp() *schema.Resource {
	return &schema.Resource{
		Read:   resourceOauth2AppRead,
		Create: resourceOauth2AppUpcreate,
		Update: resourceOauth2AppUpcreate,
		Delete: resourceOauth2AppDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},
		Schema: map[string]*schema.Schema{
			oauth2KeyName: {
				Required:    true,
				Type:        schema.TypeString,
				Description: "OAuth Application name",
			},
			oauth2KeyRedirectURIs: {
				Required: true,
				Type:     schema.TypeSet,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
				Description: "Accepted redirect URIs",
			},
			oauth2KeyClientId: {
				Type:        schema.TypeString,
				Computed:    true,
				Description: "OAuth2 Application client id",
			},
			oauth2KeyClientSecret: {
				Type:        schema.TypeString,
				Computed:    true,
				Sensitive:   true,
				Description: "Oauth2 Application client secret",
			},
		},
		Description: "Handling [gitea oauth application](https://docs.gitea.io/en-us/oauth2-provider/) resources",
	}
}

func ExpandStringList(configured []interface{}) []string {
	res := make([]string, 0, len(configured))
	for _, v := range configured {
		val, ok := v.(string)
		if ok && val != "" {
			res = append(res, (v.(string)))
		}
	}
	return res
}

func CollapseStringList(strlist []string) []interface{} {
	res := make([]interface{}, 0, len(strlist))
	for _, v := range strlist {
		res = append(res, v)
	}
	return res
}

func resourceOauth2AppUpcreate(d *schema.ResourceData, meta interface{}) (err error) {
	client := meta.(*gitea.Client)

	redirectURIsSchema, redirectURIsSchemaOk := d.Get(oauth2KeyRedirectURIs).(*schema.Set)

	if !redirectURIsSchemaOk {
		return fmt.Errorf("attribute %s must be set to a set of strings", oauth2KeyRedirectURIs)
	}

	redirectURIs := ExpandStringList(redirectURIsSchema.List())

	name, nameOk := d.Get(oauth2KeyName).(string)

	if !nameOk {
		return fmt.Errorf("attribute %s must be set and must be a string", oauth2KeyName)
	}

	opts := gitea.CreateOauth2Option{
		Name:         name,
		RedirectURIs: redirectURIs,
	}

	var oauth2 *gitea.Oauth2

	if d.IsNewResource() {
		oauth2, _, err = client.CreateOauth2(opts)
	} else {
		oauth2, err := searchOauth2AppByClientId(client, d.Id())

		if err != nil {
			return err
		}

		oauth2, _, err = client.UpdateOauth2(oauth2.ID, opts)
	}

	if err != nil {
		return
	}

	err = setOAuth2ResourceData(oauth2, d)

	return
}

func searchOauth2AppByClientId(c *gitea.Client, id string) (res *gitea.Oauth2, err error) {
	page := 1

	for {
		apps, _, err := c.ListOauth2(gitea.ListOauth2Option{
			ListOptions: gitea.ListOptions{
				Page:     page,
				PageSize: 50,
			},
		})
		if err != nil {
			return nil, err
		}
		if len(apps) == 0 {
			return nil, fmt.Errorf("no oauth client can be found by id '%s'", id)
		}

		for _, app := range apps {
			if app.ClientID == id {
				return app, nil
			}
		}

		page += 1
	}
}

func resourceOauth2AppRead(d *schema.ResourceData, meta interface{}) (err error) {
	client := meta.(*gitea.Client)

	app, err := searchOauth2AppByClientId(client, d.Id())

	if err != nil {
		return err
	}

	err = setOAuth2ResourceData(app, d)

	return
}

func resourceOauth2AppDelete(d *schema.ResourceData, meta interface{}) (err error) {
	client := meta.(*gitea.Client)

	app, err := searchOauth2AppByClientId(client, d.Id())

	if err != nil {
		return err
	}

	_, err = client.DeleteOauth2(app.ID)

	return
}

func setOAuth2ResourceData(app *gitea.Oauth2, d *schema.ResourceData) (err error) {
	d.SetId(app.ClientID)

	for k, v := range map[string]interface{}{
		oauth2KeyName:         app.Name,
		oauth2KeyRedirectURIs: schema.NewSet(schema.HashString, CollapseStringList(app.RedirectURIs)),
		oauth2KeyClientId:     app.ClientID,
	} {
		err = d.Set(k, v)
		if err != nil {
			return
		}
	}

	if app.ClientSecret != "" {
		// Gitea API only reports client secrets if the resource is newly created
		d.Set(oauth2KeyClientSecret, app.ClientSecret)
	}

	return
}
