package image

import (
	"context"
	"strconv"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	log "github.com/sirupsen/logrus"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudbroker/image"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/constants"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/controller"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/dc"
	"repository.basistech.ru/BASIS/terraform-provider-decort/internal/status"
)

func resourceMultiImageCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceMultiImageCreate: called for image %s", d.Get("name").(string))

	c := m.(*controller.ControllerCfg)

	targetIDsRaw := d.Get("target_ids").([]interface{})
	targetIDs := make([]uint64, 0, len(targetIDsRaw))
	for _, id := range targetIDsRaw {
		targetIDs = append(targetIDs, uint64(id.(int)))
	}

	req := image.CreateMultiImageRequest{
		Name:      d.Get("name").(string),
		TargetIDs: targetIDs,
		AccountID: uint64(d.Get("account_id").(int)),
	}

	imageId, err := c.CloudBroker().Image().CreateMultiImage(ctx, req)
	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId(strconv.FormatUint(imageId, 10))
	d.Set("image_id", imageId)

	var w dc.Warnings

	if !d.Get("enabled").(bool) {
		if err := resourceImageChangeEnabled(ctx, d, m); err != nil {
			w.Add(err)
		}
	}

	if _, ok := d.GetOk("shared_with"); ok {
		if err := resourceImageShare(ctx, d, m); err != nil {
			w.Add(err)
		}
	}

	if _, ok := d.GetOk("computeci_id"); ok {
		if err := resourceImageChangeComputeci(ctx, d, m); err != nil {
			w.Add(err)
		}
	}

	updReq := image.EditRequest{}
	updReq.ImageID = uint64(d.Get("image_id").(int))

	if _, ok := d.GetOk("username"); ok {
		updReq.Username = d.Get("username").(string)
	}
	if _, ok := d.GetOk("password"); ok {
		updReq.Password = d.Get("password").(string)
	}
	if _, ok := d.GetOk("account_id"); ok {
		updReq.AccountID = uint64(d.Get("account_id").(int))
	}
	if _, ok := d.GetOkExists("bootable"); ok {
		updReq.Bootable = d.Get("bootable").(bool)
	}
	if _, ok := d.GetOkExists("hot_resize"); ok {
		updReq.HotResize = d.Get("hot_resize").(bool)
	}
	if _, ok := d.GetOk("network_interface_naming"); ok {
		updReq.NetworkInterfaceNaming = d.Get("network_interface_naming").(string)
	}
	_, err = c.CloudBroker().Image().Edit(ctx, updReq)
	if err != nil {
		w.Add(err)
	}

	return append(resourceMultiImageRead(ctx, d, m), w.Get()...)
}

func resourceMultiImageRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceMultiImageRead: called for %s id: %s", d.Get("name").(string), d.Id())

	img, err := utilityImageCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	switch img.Status {
	case status.Modeled:
		return diag.Errorf("The image is in status: %s, please, contact support for more information", img.Status)
	case status.Destroyed, status.Purged:
		d.SetId("")
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
	}

	flattenImage(d, img)

	return nil
}

func resourceMultiImageDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceMultiImageDelete: called for %s, id: %s", d.Get("name").(string), d.Id())

	_, err := utilityImageCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	c := m.(*controller.ControllerCfg)
	req := image.DeleteRequest{
		ImageID: uint64(d.Get("image_id").(int)),
	}

	_, err = c.CloudBroker().Image().Delete(ctx, req)
	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId("")

	return nil
}

func resourceMultiImageUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceMultiImageUpdate: called for %s, id: %s", d.Get("name").(string), d.Id())

	img, err := utilityImageCheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	switch img.Status {
	case status.Modeled:
		return diag.Errorf("The image is in status: %s, please, contact support for more information", img.Status)
	case status.Destroyed, status.Purged:
		d.SetId("")
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
	}

	if d.HasChange("enabled") {
		err := resourceImageChangeEnabled(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("shared_with") {
		err := resourceImageShare(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("computeci_id") {
		err := resourceImageChangeComputeci(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChanges("name", "username", "password", "account_id", "bootable", "hot_resize") {
		err := resourceImageEdit(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	if d.HasChange("target_ids") {
		err := resourceMultiImageUpdateLinks(ctx, d, m)
		if err != nil {
			return diag.FromErr(err)
		}
	}

	return resourceMultiImageRead(ctx, d, m)
}

func resourceMultiImageUpdateLinks(ctx context.Context, d *schema.ResourceData, m interface{}) error {
	log.Debugf("resourceMultiImageUpdateLinks: called for %s, id: %s", d.Get("name").(string), d.Id())
	c := m.(*controller.ControllerCfg)

	imageID := uint64(d.Get("image_id").(int))

	oldRaw, newRaw := d.GetChange("target_ids")
	oldIDs := oldRaw.([]interface{})
	newIDs := newRaw.([]interface{})

	oldSet := make(map[uint64]bool)
	for _, id := range oldIDs {
		oldSet[uint64(id.(int))] = true
	}

	newSet := make(map[uint64]bool)
	for _, id := range newIDs {
		newSet[uint64(id.(int))] = true
	}

	toAdd := make([]uint64, 0)
	for id := range newSet {
		if !oldSet[id] {
			toAdd = append(toAdd, id)
		}
	}

	toRemove := make([]uint64, 0)
	for id := range oldSet {
		if !newSet[id] {
			toRemove = append(toRemove, id)
		}
	}

	if len(toAdd) > 0 {
		req := image.MultiImageAddLinksRequest{
			ImageID:   imageID,
			TargetIDs: toAdd,
		}
		_, err := c.CloudBroker().Image().MultiImageAddLinks(ctx, req)
		if err != nil {
			return err
		}
	}

	if len(toRemove) > 0 {
		req := image.MultiImageDelLinksRequest{
			ImageID:   imageID,
			TargetIDs: toRemove,
		}
		_, err := c.CloudBroker().Image().MultiImageDelLinks(ctx, req)
		if err != nil {
			return err
		}
	}

	return nil
}

func ResourceMultiImage() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 2,

		CreateContext: resourceMultiImageCreate,
		ReadContext:   resourceMultiImageRead,
		UpdateContext: resourceMultiImageUpdate,
		DeleteContext: resourceMultiImageDelete,

		Importer: &schema.ResourceImporter{
			StateContext: schema.ImportStatePassthroughContext,
		},

		Timeouts: &schema.ResourceTimeout{
			Create:  &constants.Timeout600s,
			Read:    &constants.Timeout300s,
			Update:  &constants.Timeout300s,
			Delete:  &constants.Timeout300s,
			Default: &constants.Timeout300s,
		},

		Schema: resourceMultiImageSchemaMake(),
	}
}
