package main

import (
	"code.justin.tv/dta/necronomicon-user-api/app"
	"code.justin.tv/dta/necronomicon-user-api/app/test"
	"encoding/json"
	"fmt"
	"github.com/goadesign/goa"
	"github.com/goamz/goamz/s3"
	"golang.org/x/net/context"
	"strconv"
	"strings"
	"testing"
)

type BucketMock struct {
}

func (mock BucketMock) Put(path string, data []byte, contType string, perm s3.ACL, options s3.Options) error {
	return nil
}

func setupDeploymentTest(t *testing.T) (db *DB, snapshotId int, service *goa.Service, ctrl *DeploymentController, ctx context.Context) {
	db, snapshotId, err := CreateLotrSnapshots(t)
	if err != nil {
		t.Fatalf("Couldn't setup database for test")
		return
	}
	service = goa.New("necronomicon-user-api-test")
	resources := new(Resources)
	resources.db = db
	resources.bucket = BucketMock{}
	ctrl = NewDeploymentController(service)
	ctrl.resources = resources
	ctx = context.Background()
	return
}

func createDeploymentTest(t *testing.T, db *DB, snapshotId int) int {
	d := DeploymentModel{SnapshotID: snapshotId, User: "testuser", Message: "test message"}
	deploymentId, err := db.CreateDeployment(&d)
	if err != nil {
		t.Fatalf("Couldn't create deployment: %s", err)
	}
	return deploymentId
}

func TestListDeploymentOK(t *testing.T) {
	db, snapshotId, service, ctrl, ctx := setupDeploymentTest(t)
	deploymentId := createDeploymentTest(t, db, snapshotId)
	_, deployments := test.ListDeploymentOK(t, ctx, service, ctrl, nil, nil, 100, nil)
	if len(deployments) != 1 {
		t.Fatalf("Expected 1 deployment, got %d instead", len(deployments))
	}
	if deployments[0].DeploymentID != deploymentId {
		t.Fatalf("Expected deployment ID to be %d, got %d instead", deploymentId, deployments[0].DeploymentID)
	}
}

func TestShowDeploymentOK(t *testing.T) {
	db, snapshotId, service, ctrl, ctx := setupDeploymentTest(t)
	deploymentId := createDeploymentTest(t, db, snapshotId)
	_, deployment := test.ShowDeploymentOK(t, ctx, service, ctrl, deploymentId, nil)
	if deployment.DeploymentID != deploymentId {
		t.Fatalf("Expected deployment ID to be %d, got %d instead", deploymentId, deployment.DeploymentID)
	}
}

func TestShowDeploymentNotFound(t *testing.T) {
	_, _, service, ctrl, ctx := setupDeploymentTest(t)
	test.ShowDeploymentNotFound(t, ctx, service, ctrl, 999, nil)
}

func TestCreateDeploymentBadRequest(t *testing.T) {
	_, snapshotId, service, ctrl, ctx := setupDeploymentTest(t)
	payload := app.DeploymentPayload{}
	payloadStr := fmt.Sprintf(`{"message": "test", "snapshot_id": %d}`, snapshotId+999)
	err := json.Unmarshal([]byte(payloadStr), &payload)
	if err != nil {
		t.Fatalf("Couldn't unmarshal payload: %+v", payload)
	}
	test.CreateDeploymentBadRequest(t, ctx, service, ctrl, nil, &payload)
}

func TestCreateDeploymentCreated(t *testing.T) {
	db, snapshotId, service, ctrl, ctx := setupDeploymentTest(t)
	payload := app.DeploymentPayload{}
	payloadStr := fmt.Sprintf(`{"message": "test", "snapshot_id": %d}`, snapshotId)
	err := json.Unmarshal([]byte(payloadStr), &payload)
	if err != nil {
		t.Fatalf("Couldn't unmarshal payload: %+v", payload)
	}
	rw := test.CreateDeploymentCreated(t, ctx, service, ctrl, nil, &payload)
	loc := rw.Header().Get("Location")
	if loc == "" {
		t.Fatalf("Missing location header!")
	}
	elems := strings.Split(loc, "/")
	deploymentId, err := strconv.Atoi(elems[len(elems)-1])
	if err != nil {
		t.Fatalf("Invalid location header %v, must end with id", loc)
	}
	deployment, err := db.GetDeployment(deploymentId)
	if err != nil {
		t.Fatalf("Couldn't get deployment ID=%d from the database", deploymentId)
	}
	if deployment.ID != deploymentId || deployment.Message != "test" {
		t.Fatalf("Got an invalid deployment: %+v", deployment)
	}
}

func TestPreviewDeploymentOK(t *testing.T) {
	db, snapshotId, service, ctrl, ctx := setupDeploymentTest(t)
	payload := app.DeploymentPayload{}
	payloadStr := fmt.Sprintf(`{"message": "test", "snapshot_id": %d}`, snapshotId)
	err := json.Unmarshal([]byte(payloadStr), &payload)
	if err != nil {
		t.Fatalf("Couldn't unmarshal payload: %+v", payload)
	}

	snapshot, err := db.GetSnapshot(snapshotId)
	if err != nil {
		t.Fatalf("Couldn't get snapshot ID=%d from the database", snapshotId)
	}
	fmt.Printf("snapshot: %+v\n", snapshot)

	_, state := test.PreviewDeploymentOK(t, ctx, service, ctrl, nil, &payload)
	vals := state.Map["staging"].Values["myapp"]
	if len(vals) != 3 || vals["x"] != 15.0 || vals["y"] != 2.0 || vals["z"] != 16.0 {
		t.Errorf("Error previewing. Expected {x: 15, y: 2, z: 16}, got %+v instead", vals)
	}
}
