package chroot

import (
	"context"
	"io/ioutil"
	"net/http"
	"reflect"
	"regexp"
	"strings"
	"testing"

	"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-12-01/compute"
	"github.com/Azure/go-autorest/autorest"
	"github.com/hashicorp/packer/builder/azure/common/client"
	"github.com/hashicorp/packer/helper/multistep"
	"github.com/hashicorp/packer/packer"
)

func TestStepVerifySharedImageSource_Run(t *testing.T) {
	type fields struct {
		SharedImageID  string
		SubscriptionID string
		Location       string
	}
	tests := []struct {
		name    string
		fields  fields
		want    multistep.StepAction
		wantErr string
	}{
		{
			name: "happy path",
			fields: fields{
				SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.2.3",
				Location:      "VM location",
			},
		},
		{
			name: "resource is not a shared image",
			fields: fields{
				SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/disks/myDisk",
				Location:      "VM location",
			},
			want:    multistep.ActionHalt,
			wantErr: "does not identify a shared image version",
		},
		{
			name: "error in resource id",
			fields: fields{
				SharedImageID: "not-a-resource-id",
			},
			want:    multistep.ActionHalt,
			wantErr: "Could not parse resource id",
		},
		{
			name: "wrong location",
			fields: fields{
				SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/1.2.3",
				Location:      "other location",
			},
			want:    multistep.ActionHalt,
			wantErr: "does not include VM location",
		},
		{
			name: "image not found",
			fields: fields{
				SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/2.3.4",
				Location:      "vm location",
			},
			want:    multistep.ActionHalt,
			wantErr: "Error retrieving shared image version",
		},
		{
			name: "windows image",
			fields: fields{
				SharedImageID: "/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/windowsImage/versions/1.2.3",
				Location:      "VM location",
			},
			want:    multistep.ActionHalt,
			wantErr: "not a Linux image",
		},
	}
	for _, tt := range tests {
		giv := compute.NewGalleryImageVersionsClient("subscriptionID")
		giv.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
			if r.Method == "GET" {
				switch {
				case strings.HasSuffix(r.URL.Path, "/versions/1.2.3"):
					return &http.Response{
						Request: r,
						Body: ioutil.NopCloser(strings.NewReader(`{
								"id": "image-version-id",
								"properties": {
									"publishingProfile": {
										"targetRegions": [
											{ "name": "vm Location" }
										]
									}
								}
							}`)),
						StatusCode: 200,
					}, nil
				case regexp.MustCompile(`(?i)^/subscriptions/subscriptionID/resourceGroups/rg/providers/Microsoft.Compute/galleries/myGallery/images/myImage/versions/\d+\.\d+\.\d+$`).
					MatchString(r.URL.Path):
					return &http.Response{
						Request:    r,
						Body:       ioutil.NopCloser(strings.NewReader(`{"error":{"code":"NotFound"}}`)),
						StatusCode: 404,
					}, nil
				}
			}

			t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI())
			return &http.Response{
				Request:    r,
				Status:     "Unexpected HTTP call",
				Body:       ioutil.NopCloser(strings.NewReader(`{"code":"TestError"}`)),
				StatusCode: 599,
			}, nil
		})

		gi := compute.NewGalleryImagesClient("subscriptionID")
		gi.Sender = autorest.SenderFunc(func(r *http.Request) (*http.Response, error) {
			if r.Method == "GET" {
				switch {
				case strings.HasSuffix(r.URL.Path, "/images/myImage"):
					return &http.Response{
						Request: r,
						Body: ioutil.NopCloser(strings.NewReader(`{
						"id": "image-id",
						"properties": {
							"osType": "Linux"
						}
					}`)),
						StatusCode: 200,
					}, nil
				case strings.HasSuffix(r.URL.Path, "/images/windowsImage"):
					return &http.Response{
						Request: r,
						Body: ioutil.NopCloser(strings.NewReader(`{
							"id": "image-id",
							"properties": {
								"osType": "Windows"
							}
						}`)),
						StatusCode: 200,
					}, nil
				}
			}

			t.Errorf("Unexpected HTTP call: %s %s", r.Method, r.URL.RequestURI())
			return &http.Response{
				Request:    r,
				Status:     "Unexpected HTTP call",
				Body:       ioutil.NopCloser(strings.NewReader(`{"error":{"code":"TestError"}}`)),
				StatusCode: 599,
			}, nil
		})

		state := new(multistep.BasicStateBag)
		state.Put("azureclient", &client.AzureClientSetMock{
			SubscriptionIDMock:             "subscriptionID",
			GalleryImageVersionsClientMock: giv,
			GalleryImagesClientMock:        gi,
		})
		state.Put("ui", packer.TestUi(t))

		t.Run(tt.name, func(t *testing.T) {
			s := &StepVerifySharedImageSource{
				SharedImageID:  tt.fields.SharedImageID,
				SubscriptionID: tt.fields.SubscriptionID,
				Location:       tt.fields.Location,
			}
			if got := s.Run(context.TODO(), state); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("StepVerifySharedImageSource.Run() = %v, want %v", got, tt.want)
			}
			d, _ := state.GetOk("error")
			err, _ := d.(error)
			if tt.wantErr != "" {
				if !strings.Contains(err.Error(), tt.wantErr) {
					t.Errorf("Wanted error %q, got %q", tt.wantErr, err)
				}
			} else if err != nil && err.Error() != "" {
				t.Errorf("Unexpected error: %v", err)
			}
		})
	}
}
