package utilities

import (
	"context"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/terraform-plugin-framework/diag"
	"github.com/hashicorp/terraform-plugin-framework/types"
	"github.com/hashicorp/terraform-plugin-log/tflog"

	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/k8s"
	"repository.basistech.ru/BASIS/decort-golang-sdk/pkg/cloudapi/tasks"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/k8s/models"
)

func CreateRequestResourceK8WG(ctx context.Context, plan *models.ResourceK8SWGModel) k8s.WorkersGroupAddRequest {
	tflog.Info(ctx, "Start CreateRequestResourceK8WG", map[string]any{"name": plan.Name.ValueString()})
	req := k8s.WorkersGroupAddRequest{
		K8SID: uint64(plan.K8SID.ValueInt64()),
		Name:  plan.Name.ValueString(),
	}

	if !plan.WorkerSEPID.IsNull() {
		req.WorkerSEPID = uint64(plan.WorkerSEPID.ValueInt64())
	} else {
		req.WorkerSEPID = 0
	}
	if !plan.WorkerSEPPool.IsNull() {
		req.WorkerSEPPool = plan.WorkerSEPPool.ValueString()
	}
	if !plan.Num.IsUnknown() {
		req.WorkerNum = uint64(plan.Num.ValueInt64())
	} else {
		req.WorkerNum = 1
	}
	if !plan.CPU.IsUnknown() {
		req.WorkerCPU = uint64(plan.CPU.ValueInt64())
	} else {
		req.WorkerCPU = 1
	}
	if !plan.RAM.IsUnknown() {
		req.WorkerRAM = uint64(plan.RAM.ValueInt64())
	} else {
		req.WorkerRAM = 1024
	}
	if !plan.Disk.IsUnknown() {
		req.WorkerDisk = uint64(plan.Disk.ValueInt64())
	} else {
		req.WorkerDisk = 0
	}
	if !plan.Annotations.IsUnknown() {
		result := make([]string, 0, len(plan.Annotations.Elements()))
		for _, val := range plan.Annotations.Elements() {
			result = append(result, strings.Trim(val.String(), "\""))
		}
		req.Annotations = result
	}
	if !plan.Labels.IsUnknown() {
		result := make([]string, 0, len(plan.Labels.Elements()))
		for _, val := range plan.Labels.Elements() {
			result = append(result, strings.Trim(val.String(), "\""))
		}

		req.Labels = result
	}
	if !plan.Taints.IsUnknown() {
		result := make([]string, 0, len(plan.Taints.Elements()))
		for _, val := range plan.Taints.Elements() {
			result = append(result, strings.Trim(val.String(), "\""))
		}
		req.Taints = result
	}
	if !plan.CloudInit.IsNull() {
		req.UserData = plan.CloudInit.ValueString()
	}
	if !plan.Chipset.IsNull() {
		req.Chipset = plan.Chipset.ValueString()
	}

	tflog.Info(ctx, "End CreateRequestResourceK8WG", map[string]any{"name": plan.Name.ValueString()})
	return req
}

func ResourceK8SWGCreate(ctx context.Context, plan *models.ResourceK8SWGModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start ResourceK8SWGCreate", map[string]any{"name": plan.Name.ValueString(), "k8s_id": plan.K8SID.ValueInt64()})

	diags := diag.Diagnostics{}

	// Make request and get response
	resp, err := c.CloudAPI().K8S().WorkersGroupAdd(ctx, CreateRequestResourceK8WG(ctx, plan))
	if err != nil {
		tflog.Error(ctx, "Error response for create k8s_wg")
		diags.AddError("Unable to Create K8SWG", err.Error())
		return diags
	}

	taskReq := tasks.GetRequest{
		AuditID: strings.Trim(resp, `"`),
	}
	for {
		task, err := c.CloudAPI().Tasks().Get(ctx, taskReq)
		if err != nil {
			diags.AddError("The audit cannot be found", err.Error())
			return diags
		}

		tflog.Info(ctx, fmt.Sprintf("ResourceK8SWGCreate instance creating - %s", task.Stage))

		if task.Completed {
			if task.Error != "" {
				diags.AddError("Cannot create k8s wg instance:", task.Error)
				return diags
			}

			result, err := task.Result.ID()
			if err != nil {
				diags.AddError("Cannot get wg ID:", err.Error())
				return diags
			}
			plan.Id = types.StringValue(strconv.Itoa(result))
			plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))
			break
		}

		time.Sleep(time.Second * 20)
	}

	plan.LastUpdated = types.StringValue(time.Now().Format(time.RFC850))

	wg, k8sId, err := K8SWGResourceCheckPresence(ctx, plan, c)
	if err != nil {
		diags.AddError("Cannot get info about wg ", err.Error())
		return diags
	}

	tflog.Info(ctx, "End ResourceK8SWGCreate", map[string]any{"k8s_id": k8sId, "k8s_wg": wg.ID})
	return diags
}

func K8SWGResourceCheckPresence(ctx context.Context, plan *models.ResourceK8SWGModel, c *client.Client) (*k8s.ItemK8SGroup, uint64, error) {
	tflog.Info(ctx, "Get info about wg with ID", map[string]any{"wg_id": plan.Id.ValueString()})

	var wgId int
	var k8sId int
	var err error

	ids := strings.Split(plan.Id.ValueString(), "#")
	if len(ids) == 2 {
		k8sId, err = strconv.Atoi(ids[0])
		if err != nil {
			return nil, 0, err
		}
		wgId, err = strconv.Atoi(ids[1])
		if err != nil {
			return nil, 0, err
		}
	} else {
		wgId, err = strconv.Atoi(plan.Id.ValueString())
		if err != nil {
			return nil, 0, err
		}
		k8sId = int(plan.K8SID.ValueInt64())
	}

	cluster, err := c.CloudAPI().K8S().Get(ctx, k8s.GetRequest{K8SID: uint64(k8sId)})
	if err != nil {
		return nil, 0, fmt.Errorf("cannot get info about cluster with error: %w", err)
	}

	for _, wg := range cluster.K8SGroups.Workers {
		if wg.ID == uint64(wgId) {
			tflog.Info(ctx, "Getting info about wg successfully", map[string]any{"wg_id": plan.Id.ValueString()})
			return &wg, cluster.ID, nil
		}
	}

	tflog.Error(ctx, fmt.Sprintf("Getting info about wg end with error - %s", err.Error()))
	return nil, 0, fmt.Errorf("not found wg with id: %d in k8s cluster: %d", wgId, cluster.ID)
}

func K8SWGUpdateCloudInit(ctx context.Context, plan, state *models.ResourceK8SWGModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Update cloud init in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})

	diags := diag.Diagnostics{}

	req := k8s.UpdateWorkerNodesMetaDataRequest{
		K8SID:          uint64(state.K8SID.ValueInt64()),
		WorkersGroupID: uint64(state.WorkerGroupId.ValueInt64()),
		UserData:       plan.CloudInit.ValueString(),
	}

	_, err := c.CloudAPI().K8S().UpdateWorkerNodesMetaData(ctx, req)
	if err != nil {
		diags.AddError(fmt.Sprintf("Cannot update cloud init in wg with id: %d", state.WorkerGroupId.ValueInt64()), err.Error())
		return diags
	}

	tflog.Info(ctx, "Update cloud init successfully in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})
	return diags
}

func K8SWGUpdateNumWorkers(ctx context.Context, plan, state *models.ResourceK8SWGModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Update num workers in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})

	diags := diag.Diagnostics{}

	wg, k8sId, err := K8SWGResourceCheckPresence(ctx, plan, c)
	if err != nil {
		diags.AddError("Cannot get info about wg ", err.Error())
		return diags
	}

	if newNum := plan.Num.ValueInt64(); uint64(newNum) > wg.Num {
		req := k8s.WorkerAddRequest{
			K8SID:          k8sId,
			WorkersGroupID: wg.ID,
			Num:            uint64(newNum) - wg.Num,
			Chipset:        plan.Chipset.ValueString(),
		}

		tflog.Info(ctx, "Add workers in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})

		_, err := c.CloudAPI().K8S().WorkerAdd(ctx, req)
		if err != nil {
			diags.AddError(fmt.Sprintf("Cannot add workers in wg with id: %d", state.WorkerGroupId.ValueInt64()), err.Error())
			return diags
		}

		tflog.Info(ctx, "Add workers successfully in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})
	} else {
		for i := int64(wg.Num) - 1; i >= newNum; i-- {
			req := k8s.DeleteWorkerFromGroupRequest{
				K8SID:          k8sId,
				WorkersGroupID: wg.ID,
				WorkerID:       wg.DetailedInfo[i].ID,
			}
			tflog.Info(ctx, "Start delete worker in wg with id", map[string]any{"worker_id": wg.DetailedInfo[i].ID, "wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})

			_, err := c.CloudAPI().K8S().DeleteWorkerFromGroup(ctx, req)
			if err != nil {
				diags.AddError(fmt.Sprintf("Cannot delete worker from wg with id: %d", state.WorkerGroupId.ValueInt64()), err.Error())
				return diags
			}

			tflog.Info(ctx, "End delete worker successfully in wg with id", map[string]any{"worker_id": wg.DetailedInfo[i].ID, "wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})
		}
	}

	tflog.Info(ctx, "Update num workers successfully in wg with id", map[string]any{"wg_id": state.WorkerGroupId.ValueInt64(), "k8s_id": state.K8SID.ValueInt64()})
	return diags
}
