package fixtures

import (
	"bytes"
	"context"
	"encoding/json"
	"io/ioutil"
	"text/template"

	"crypto/x509/pkix"

	"github.com/RafayLabs/rcloud-base/pkg/sentry/cryptoutil"
	"github.com/RafayLabs/rcloud-base/pkg/service"
	"github.com/rs/xid"
	"sigs.k8s.io/yaml"

	"github.com/RafayLabs/rcloud-base/pkg/log"
	commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
	sentry "github.com/RafayLabs/rcloud-base/proto/types/sentry"
	"github.com/shurcooL/httpfs/vfsutil"
)

var _log = log.GetLogger()

var (
	// RelayTemplate is the template for rendering download yaml
	RelayTemplate *template.Template
	// RelayAgentTemplate is the template for rendering download yaml
	RelayAgentTemplate *template.Template
)

// XXX Warning : loadAgentTemplates XXX
// Changes to the below code will affect the default template
// and risks CA cert update. Once the template certs are
// updated following situation will arise.
// - Existing relay connections will fail, need to restart
//   agents to re-bootstrap.
// - Existing kubeconfig will fail, need to download new
//   kubeconfig to continue
// This can cause issues in the case of production clusters
// Take extra caution while modifying the code to avoid
// unintended side effects.
func loadAgentTemplates(ctx context.Context, bs service.BootstrapService, d map[string]interface{}, pf cryptoutil.PasswordFunc) error {
	yb, err := vfsutil.ReadFile(Fixtures, "agent_templates.yaml")
	if err != nil {
		return err
	}

	t, err := template.New("agent_templates.yaml").Parse(string(yb))
	if err != nil {
		return err
	}

	bb := new(bytes.Buffer)

	err = t.Execute(bb, d)
	if err != nil {
		return err
	}

	var agentTemplateList sentry.BootstrapAgentTemplateList

	jb, err := yaml.YAMLToJSONStrict(bb.Bytes())
	if err != nil {
		return err
	}

	err = json.Unmarshal(jb, &agentTemplateList)
	if err != nil {
		return err
	}

	for _, item := range agentTemplateList.Items {
		// Check bootstrap infr entry exist
		bInfra, _ := bs.GetBootstrapInfra(ctx, item.Spec.InfraRef)
		if bInfra != nil && bInfra.Spec.CaCert != "" {
			// Skip updating bootstrap infra
			_log.Infow("loadAgentTemplates", "skip bootstrap template creation, entry exist", item.Spec.InfraRef)
		} else {
			// Create bootstrap infra entry
			cert, key, err := cryptoutil.GenerateCA(pkix.Name{
				CommonName:         item.Spec.InfraRef,
				Country:            []string{"USA"},
				Organization:       []string{"Rafay Systems Inc"},
				OrganizationalUnit: []string{"Rafay Sentry"},
				Province:           []string{"California"},
				Locality:           []string{"Sunnyvale"},
			}, pf)
			if err != nil {
				return err
			}
			err = bs.PatchBootstrapInfra(ctx, &sentry.BootstrapInfra{
				Metadata: &commonv3.Metadata{
					Name: item.Spec.InfraRef,
				},
				Spec: &sentry.BootstrapInfraSpec{
					CaCert: string(cert),
					CaKey:  string(key),
				},
			})

			if err != nil {
				return err
			}
		}

		item.Spec.Token = xid.New().String()
		// Create/Update bootstrap agent template
		// Token is not updated if entry already exist.
		err = bs.PatchBootstrapAgentTemplate(ctx, item)
		if err != nil {
			return err
		}

	}

	return nil
}

// Load loads fixtures
func Load(ctx context.Context, bs service.BootstrapService, d map[string]interface{}, pf cryptoutil.PasswordFunc) error {
	err := loadAgentTemplates(ctx, bs, d, pf)
	if err != nil {
		return err
	}
	service.KEKFunc = pf

	err = loadRelayTemplate()

	return err
}

func loadRelayTemplate() error {
	f, err := Fixtures.Open("relay_template.yaml")
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}

	RelayTemplate, err = template.New("relay_template").Parse(string(b))
	if err != nil {
		return err
	}

	f1, err := Fixtures.Open("relay_agent_template.yaml")
	if err != nil {
		return err
	}

	b1, err := ioutil.ReadAll(f1)
	if err != nil {
		return err
	}

	RelayAgentTemplate, err = template.New("relay_agent_template").Parse(string(b1))
	return err
}
