// © Broadcom. All Rights Reserved.
// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
// SPDX-License-Identifier: Apache-2.0

package simulator

import (
	"time"

	"github.com/vmware/govmomi/vim25/methods"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/soap"
	"github.com/vmware/govmomi/vim25/types"
)

var ExtensionList = []types.Extension{
	{
		Description: &types.Description{
			Label:   "vcsim",
			Summary: "Go vCenter simulator",
		},
		Key:         "com.vmware.govmomi.simulator",
		Company:     "VMware, Inc.",
		Type:        "",
		Version:     "0.37.0",
		SubjectName: "",
		Server:      nil,
		Client:      nil,
		TaskList: []types.ExtensionTaskTypeInfo{
			{
				TaskID: "com.vmware.govmomi.simulator.test",
			},
		},
		EventList:              nil,
		FaultList:              nil,
		PrivilegeList:          nil,
		ResourceList:           nil,
		LastHeartbeatTime:      time.Now(),
		HealthInfo:             (*types.ExtensionHealthInfo)(nil),
		OvfConsumerInfo:        (*types.ExtensionOvfConsumerInfo)(nil),
		ExtendedProductInfo:    (*types.ExtExtendedProductInfo)(nil),
		ManagedEntityInfo:      nil,
		ShownInSolutionManager: types.NewBool(false),
		SolutionManagerInfo:    (*types.ExtSolutionManagerInfo)(nil),
	},
}

type ExtensionManager struct {
	mo.ExtensionManager
}

func (m *ExtensionManager) init(r *Registry) {
	if r.IsVPX() && len(m.ExtensionList) == 0 {
		m.ExtensionList = ExtensionList
	}
}

func (m *ExtensionManager) FindExtension(ctx *Context, req *types.FindExtension) soap.HasFault {
	body := &methods.FindExtensionBody{
		Res: new(types.FindExtensionResponse),
	}

	for _, x := range m.ExtensionList {
		if x.Key == req.ExtensionKey {
			body.Res.Returnval = &x
			break
		}
	}

	return body
}

func (m *ExtensionManager) RegisterExtension(ctx *Context, req *types.RegisterExtension) soap.HasFault {
	body := &methods.RegisterExtensionBody{}

	for _, x := range m.ExtensionList {
		if x.Key == req.Extension.Key {
			body.Fault_ = Fault("", &types.InvalidArgument{
				InvalidProperty: "extension.key",
			})
			return body
		}
	}

	body.Res = new(types.RegisterExtensionResponse)
	m.ExtensionList = append(m.ExtensionList, req.Extension)

	f := mo.Field{Path: "extensionList", Key: req.Extension.Key}
	ctx.Update(m, []types.PropertyChange{
		{Name: f.Path, Val: m.ExtensionList},
		{Name: f.String(), Val: req.Extension, Op: types.PropertyChangeOpAdd},
	})

	return body
}

func (m *ExtensionManager) UnregisterExtension(ctx *Context, req *types.UnregisterExtension) soap.HasFault {
	body := &methods.UnregisterExtensionBody{}

	for i, x := range m.ExtensionList {
		if x.Key == req.ExtensionKey {
			m.ExtensionList = append(m.ExtensionList[:i], m.ExtensionList[i+1:]...)

			f := mo.Field{Path: "extensionList", Key: req.ExtensionKey}
			ctx.Update(m, []types.PropertyChange{
				{Name: f.Path, Val: m.ExtensionList},
				{Name: f.String(), Op: types.PropertyChangeOpRemove},
			})

			body.Res = new(types.UnregisterExtensionResponse)
			return body
		}
	}

	body.Fault_ = Fault("", new(types.NotFound))

	return body
}

func (m *ExtensionManager) UpdateExtension(ctx *Context, req *types.UpdateExtension) soap.HasFault {
	body := &methods.UpdateExtensionBody{}

	for i, x := range m.ExtensionList {
		if x.Key == req.Extension.Key {
			m.ExtensionList[i] = req.Extension

			f := mo.Field{Path: "extensionList", Key: req.Extension.Key}
			ctx.Update(m, []types.PropertyChange{
				{Name: f.Path, Val: m.ExtensionList},
				{Name: f.String(), Val: req.Extension},
			})

			body.Res = new(types.UpdateExtensionResponse)
			return body
		}
	}

	body.Fault_ = Fault("", new(types.NotFound))

	return body
}

func (m *ExtensionManager) SetExtensionCertificate(ctx *Context, req *types.SetExtensionCertificate) soap.HasFault {
	body := &methods.SetExtensionCertificateBody{}

	for _, x := range m.ExtensionList {
		if x.Key == req.ExtensionKey {
			// TODO: save req.CertificatePem for use with SessionManager.LoginExtensionByCertificate()

			body.Res = new(types.SetExtensionCertificateResponse)
			return body
		}
	}

	body.Fault_ = Fault("", new(types.NotFound))

	return body
}
