/*
Copyright (c) 2019-2023 Digital Energy Cloud Solutions LLC. All Rights Reserved.
Authors:
Petr Krutov, <petr.krutov@digitalenergy.online>
Stanislav Solovev, <spsolovev@digitalenergy.online>
Kasim Baybikov, <kmbaybikov@basistech.ru>
Sergey Kisil, <svkisil@digitalenergy.online>

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.
*/

/*
Terraform DECORT provider - manage resources provided by DECORT (Digital Energy Cloud
Orchestration Technology) with Terraform by Hashicorp.

Source code: https://repository.basistech.ru/BASIS/terraform-provider-decort

Please see README.md to learn where to place source code so that it
builds seamlessly.

Documentation: https://repository.basistech.ru/BASIS/terraform-provider-decort/wiki
*/

package k8ci

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/k8ci"
	"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 resourceK8CICreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceK8CICreate: called for k8ci %s", d.Get("name").(string))

	c := m.(*controller.ControllerCfg)

	if diags := checkParamsExistence(ctx, d, c); diags != nil {
		return diags
	}

	req := k8ci.CreateRequest{
		Name:           d.Get("name").(string),
		Version:        d.Get("version").(string),
		MaxMasterCount: uint64(d.Get("max_master_count").(int)),
		MaxWorkerCount: uint64(d.Get("max_worker_count").(int)),
		MasterImageID:  uint64(d.Get("master_image_id").(int)),
		WorkerImageID:  uint64(d.Get("worker_image_id").(int)),
	}

	var networkPlugins []string
	for _, item := range d.Get("network_plugins").([]interface{}) {
		networkPlugins = append(networkPlugins, item.(string))
	}
	req.NetworkPlugins = networkPlugins

	if desc, ok := d.GetOk("desc"); ok {
		req.Description = desc.(string)
	}
	if sharedWithInterface, ok := d.GetOk("shared_with"); ok {
		sharedWithList := sharedWithInterface.(*schema.Set).List()
		var sharedWith []uint64
		for _, item := range sharedWithList {
			sharedWith = append(sharedWith, uint64(item.(int)))
		}
		req.SharedWith = sharedWith
	}

	k8ciID, err := c.CloudBroker().K8CI().Create(ctx, req)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}

	d.SetId(strconv.FormatUint(k8ciID, 10))

	warnings := dc.Warnings{}

	if enabled, ok := d.GetOk("enabled"); ok {
		log.Debugf("resourceK8CICreate: enable=%t k8ci_id %d after completing its resource configuration", enabled, k8ciID)
		if enabled.(bool) {
			enabledReq := k8ci.EnableRequest{
				K8CIID: k8ciID,
			}
			if _, err := c.CloudBroker().K8CI().Enable(ctx, enabledReq); err != nil {
				warnings.Add(err)
			}
		} else {
			disableReq := k8ci.DisableRequest{
				K8CIID: k8ciID,
			}
			if _, err := c.CloudBroker().K8CI().Disable(ctx, disableReq); err != nil {
				warnings.Add(err)
			}
		}
	}

	return append(warnings.Get(), resourceK8CIRead(ctx, d, m)...)
}

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

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

	hasChanged := false

	switch k8ciRec.Status {
	case status.Deleted:
		// restoreReq := k8ci.RestoreRequest{K8CIID: k8ciRec.ID}

		// _, err := c.CloudBroker().K8CI().Restore(ctx, restoreReq)
		// if err != nil {
		// 	return diag.FromErr(err)
		// }

		// if enabled, ok := d.GetOk("enabled"); ok {
		// 	if enabled.(bool) {
		// 		enabledReq := k8ci.EnableRequest{K8CIID: k8ciRec.ID}
		// 		_, err := c.CloudBroker().K8CI().Enable(ctx, enabledReq)
		// 		if err != nil {
		// 			return diag.FromErr(err)
		// 		}
		// 	}
		// }
		// hasChanged = true
	case status.Destroyed:
		d.SetId("")
		// return resourceK8CICreate(ctx, d, m)
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
	case status.Disabled:
		log.Debugf("The k8ci is in status: %s, troubles may occur with update. Please, enable k8ci first.", k8ciRec.Status)
	case status.Redeploying:
	case status.Deleting:
	case status.Destroying:
		return diag.Errorf("The k8ci is in progress with status: %s", k8ciRec.Status)
	case status.Modeled:
		return diag.Errorf("The k8ci is in status: %s, please, contact support for more information", k8ciRec.Status)
	}

	if hasChanged {
		k8ciRec, err = utilityK8CICheckPresence(ctx, d, m)
		if err != nil {
			d.SetId("")
			return diag.FromErr(err)
		}
	}

	flattenK8CIItems(d, k8ciRec)

	return nil
}

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

	if diags := checkParamsExistence(ctx, d, c); diags != nil {
		return diags
	}

	k8ciRec, err := utilityK8CICheckPresence(ctx, d, m)
	if err != nil {
		d.SetId("")
		return diag.FromErr(err)
	}
	warnings := dc.Warnings{}

	hasChanged := false

	switch k8ciRec.Status {
	case status.Deleted:
		restoreReq := k8ci.RestoreRequest{K8CIID: k8ciRec.ID}
		_, err := c.CloudBroker().K8CI().Restore(ctx, restoreReq)
		if err != nil {
			d.SetId("")
			return diag.FromErr(err)
		}

		if enabled, ok := d.GetOk("enabled"); ok {
			if enabled.(bool) {
				enabledReq := k8ci.EnableRequest{K8CIID: k8ciRec.ID}
				_, err := c.CloudBroker().K8CI().Enable(ctx, enabledReq)
				if err != nil {
					warnings.Add(err)
				}
			} else {
				disableReq := k8ci.DisableRequest{K8CIID: k8ciRec.ID}
				if _, err := c.CloudBroker().K8CI().Disable(ctx, disableReq); err != nil {
					warnings.Add(err)
				}
			}
		}
		hasChanged = true
	case status.Destroyed:
		d.SetId("")
		return diag.Errorf("The resource cannot be updated because it has been destroyed")
		// return resourceK8CICreate(ctx, d, m)
	case status.Disabled:
		log.Debugf("The k8ci is in status: %s, troubles may occur with update. Please, enable k8ci first.", k8ciRec.Status)
	case status.Redeploying:
	case status.Deleting:
	case status.Destroying:
		return diag.Errorf("The k8ci is in progress with status: %s", k8ciRec.Status)
	case status.Modeled:
		return diag.Errorf("The k8ci is in status: %s, please, contact support for more information", k8ciRec.Status)
	}

	if hasChanged {
		k8ciRec, err = utilityK8CICheckPresence(ctx, d, m)
		if err != nil {
			d.SetId("")
			return diag.FromErr(err)
		}
	}

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

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

	return append(resourceK8CIRead(ctx, d, m), warnings.Get()...)
}

func resourceK8CIDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics {
	log.Debugf("resourceK8CIDelete: called for k8ci %s, id: %s", d.Get("name").(string), d.Id())
	c := m.(*controller.ControllerCfg)
	k8ciId, _ := strconv.ParseUint(d.Id(), 10, 64)
	req := k8ci.DeleteRequest{
		K8CIID: k8ciId,
	}
	if permanently, ok := d.GetOk("permanently"); ok {
		req.Permanently = permanently.(bool)
	}

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

	d.SetId("")

	return nil
}

func ResourceK8CI() *schema.Resource {
	return &schema.Resource{
		SchemaVersion: 1,

		CreateContext: resourceK8CICreate,
		ReadContext:   resourceK8CIRead,
		UpdateContext: resourceK8CIUpdate,
		DeleteContext: resourceK8CIDelete,

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

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

		Schema: resourceK8CISchemaMake(),
	}
}

func resourceK8CIChangeEnable(ctx context.Context, d *schema.ResourceData, k8ciId uint64, m interface{}) error {
	c := m.(*controller.ControllerCfg)

	enabled := d.Get("enabled").(bool)
	if enabled {
		req := k8ci.EnableRequest{
			K8CIID: k8ciId,
		}
		if _, err := c.CloudBroker().K8CI().Enable(ctx, req); err != nil {
			return err
		}
	} else {
		req := k8ci.DisableRequest{
			K8CIID: k8ciId,
		}
		if _, err := c.CloudBroker().K8CI().Disable(ctx, req); err != nil {
			return err
		}
	}

	return nil
}

func resourceK8CIChangeSharedWith(ctx context.Context, d *schema.ResourceData, k8ciId uint64, m interface{}) error {
	c := m.(*controller.ControllerCfg)

	oldSet, newSet := d.GetChange("shared_with")

	deletedSharedWith := (oldSet.(*schema.Set).Difference(newSet.(*schema.Set))).List()
	if len(deletedSharedWith) > 0 {
		for _, userAcessInterface := range deletedSharedWith {
			removeReq := k8ci.AccessRemoveRequest{
				K8CIID:    k8ciId,
				AccountId: uint64(userAcessInterface.(int)),
			}

			if _, err := c.CloudBroker().K8CI().AccessRemove(ctx, removeReq); err != nil {
				return err
			}
		}
	}

	addedSharedWith := (newSet.(*schema.Set).Difference(oldSet.(*schema.Set))).List()
	if len(addedSharedWith) > 0 {
		for _, userAcessInterface := range addedSharedWith {
			removeReq := k8ci.AccessAddRequest{
				K8CIID:    k8ciId,
				AccountId: uint64(userAcessInterface.(int)),
			}

			if _, err := c.CloudBroker().K8CI().AccessAdd(ctx, removeReq); err != nil {
				return err
			}
		}
	}

	return nil
}
