package main

import (
	"context"
	"fmt"
	"net/http"
	"net/url"
	"os"

	"encoding/json"

	ory "github.com/ory/kratos-client-go"
	"github.com/ory/kratos/x"
	"github.com/peterhellberg/link"
)

var (
	KratosAdminURL string = "http://kratos-admin"
	cli                   = KratosAdminCli()
)

func KratosAdminCli() *ory.APIClient {

	conf := ory.NewConfiguration()
	conf.Servers = []ory.ServerConfiguration{{
		URL: KratosAdminURL,
	}}
	return ory.NewAPIClient(conf)
}

func SDKExitOnError(err error, res *http.Response) {
	if err == nil {
		return
	}
	var body []byte
	if res != nil {
		body, _ = json.MarshalIndent(json.RawMessage(x.MustReadAll(res.Body)), "", "  ")
	}
	out, _ := json.MarshalIndent(err, "", "  ")
	fmt.Printf("%s\n\nAn error occurred: %+v\nbody: %s\n", out, err, body)
	os.Exit(1)
}

func GetNextPageToken(response *http.Response) (string, error) {
	links := link.ParseResponse(response)
	if nextPage, ok := links["next"]; ok {
		uri, err := url.Parse(nextPage.URI)
		if err != nil {
			return "", fmt.Errorf("failed to parse nextPage uri: %w", err)
		}

		pageToken := uri.Query().Get("page_token")
		if pageToken == "" {
			return "", fmt.Errorf("next link present, but has no page_token")
		}

		return pageToken, nil
	} else {
		// There is no next page link, so we are at the last page of the response.
		return "", nil
	}
}

func AddRecoveryEmailToID(id string, email string) {

	ctx := context.Background()
	patch := []ory.JsonPatch{{
		Op:   "replace",
		Path: "/recovery_addresses",
		Value: []ory.RecoveryIdentityAddress{{
			Id:    id,
			Value: email,
			Via:   "email",
		}},
	}}

	_, resp, err := cli.IdentityAPI.PatchIdentity(ctx, id).JsonPatch(patch).Execute()
	SDKExitOnError(err, resp)
}

func ReadIdentities() ([]ory.Identity, error) {

	pageSize := int64(250)
	var pageToken string

	allids := []ory.Identity{}

	for {
		ids, resp, err := cli.IdentityAPI.ListIdentities(context.Background()).PageSize(pageSize).PageToken(pageToken).Execute()
		SDKExitOnError(err, resp)

		allids = append(allids, ids...)

		pageToken, err = GetNextPageToken(resp)
		if err != nil {
			fmt.Printf("%s\n\nAn error occurred: %+v\n", err)
			os.Exit(1)
		}

		if pageToken == "" {
			break
		}
	}

	return allids, nil
}

func main() {

	cnt := 0

	ids, err := ReadIdentities()
	if err != nil {
		fmt.Printf("error reading IDs: %+v", err)
		os.Exit(1)
	}

	for _, id := range ids {
		if len(id.RecoveryAddresses) == 0 {
			traits := id.Traits.(map[string]interface{})
			AddRecoveryEmailToID(id.Id, traits["email"].(string))
			cnt++
		}
	}

	fmt.Printf("Updated %d identities\n", cnt)
}
