/*
Copyright (c) 2017 VMware, Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package simulator_test

import (
	"context"
	"fmt"
	"log"

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

// BusyVM changes the behavior of simulator.VirtualMachine
type BusyVM struct {
	*simulator.VirtualMachine
}

// Override simulator.VirtualMachine.PowerOffVMTask to inject faults
func (vm *BusyVM) PowerOffVMTask(ctx *simulator.Context, req *types.PowerOffVM_Task) soap.HasFault {
	task := simulator.CreateTask(req.This, "powerOff", func(*simulator.Task) (types.AnyType, types.BaseMethodFault) {
		return nil, &types.TaskInProgress{}
	})

	return &methods.PowerOffVM_TaskBody{
		Res: &types.PowerOffVM_TaskResponse{
			Returnval: task.Run(ctx),
		},
	}
}

// Add AcquireTicket method, not implemented by simulator.VirtualMachine
func (vm *BusyVM) AcquireTicket(req *types.AcquireTicket) soap.HasFault {
	body := &methods.AcquireTicketBody{}

	if req.TicketType != "mks" {
		body.Fault_ = simulator.Fault("", &types.InvalidArgument{})
	}

	body.Res = &types.AcquireTicketResponse{
		Returnval: types.VirtualMachineTicket{
			Ticket: "welcome 2 the machine",
		},
	}

	return body
}

// Example of extending the simulator to inject faults.
func Example() {
	ctx := context.Background()
	model := simulator.VPX()

	defer model.Remove()
	_ = model.Create()

	s := model.Service.NewServer()
	defer s.Close()

	// NewClient connects to s.URL over https and invokes 2 SOAP methods (RetrieveServiceContent + Login)
	c, _ := govmomi.NewClient(ctx, s.URL, true)

	// Shortcut to choose any VM, rather than using the more verbose Finder or ContainerView.
	obj := simulator.Map.Any("VirtualMachine").(*simulator.VirtualMachine)
	// Validate VM is powered on
	if obj.Runtime.PowerState != "poweredOn" {
		log.Fatal(obj.Runtime.PowerState)
	}

	// Wrap the existing vm object, using the same vm.Self (ManagedObjectReference) value as the Map key.
	simulator.Map.Put(&BusyVM{obj})

	vm := object.NewVirtualMachine(c.Client, obj.Reference())

	// Start a PowerOff task using the SOAP client.
	task, _ := vm.PowerOff(ctx)

	// Wait for task completion, expecting failure.
	err := task.Wait(ctx)
	if err == nil {
		log.Fatal("expected error")
	}

	// Invalid ticket type, expecting failure.
	_, err = vm.AcquireTicket(ctx, "pks")
	if err == nil {
		log.Fatal("expected error")
	}

	mks, _ := vm.AcquireTicket(ctx, "mks")

	fmt.Println(mks.Ticket, obj.Runtime.PowerState)
	// Output: welcome 2 the machine poweredOn
}
