package flattens

import (
	"context"
	"fmt"
	"strconv"

	"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/bservice"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/client"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/bservice/models"
	"repository.basistech.ru/BASIS/terraform-provider-dynamix/internal/service/cloudapi/bservice/utilities"
)

func BServiceResource(ctx context.Context, state *models.RecordBasicServiceResourceModel, c *client.Client) diag.Diagnostics {
	tflog.Info(ctx, "Start flattens.BServiceResource")

	diags := diag.Diagnostics{}

	serviceId := uint64(state.ServiceId.ValueInt64())
	if serviceId == 0 {
		id, err := strconv.Atoi(state.ID.ValueString())
		if err != nil {
			diags.AddError(
				"flattens.BServiceResource: cannot parse resource ID from state",
				err.Error())
			return diags
		}
		serviceId = uint64(id)
	}

	recordBService, err := utilities.BServiceResourceCheckPresence(ctx, serviceId, c)
	if err != nil {
		diags.AddError(fmt.Sprintf("flattens.BServiceResource: Cannot get info about resource with ID %v", serviceId), err.Error())
		return diags
	}

	tflog.Info(ctx, "flattens.BServiceResource: before flatten", map[string]any{"service_id": serviceId, "recordBService": recordBService})

	*state = models.RecordBasicServiceResourceModel{
		Name:          types.StringValue(recordBService.Name),
		RGID:          types.Int64Value(int64(recordBService.RGID)),
		Permanently:   state.Permanently,
		Enable:        state.Enable,
		Restore:       state.Restore,
		Start:         state.Start,
		Timeouts:      state.Timeouts,
		Snapshots:     flattenSnapshot(ctx, state.Snapshots, recordBService.Snapshots),
		SSHKey:        types.StringValue(recordBService.SSHKey),
		SSHUser:       types.StringValue(recordBService.SSHUser),
		ServiceId:     types.Int64Value(int64(recordBService.ID)),
		AccountID:     types.Int64Value(int64(recordBService.AccountID)),
		Computes:      flattenComputes(ctx, recordBService.Computes),
		Groups:        flattenGroups(ctx, recordBService.Groups),
		AccountName:   types.StringValue(recordBService.Name),
		BaseDomain:    types.StringValue(recordBService.BaseDomain),
		CPUTotal:      types.Int64Value(int64(recordBService.CPUTotal)),
		CreatedBy:     types.StringValue(recordBService.CreatedBy),
		CreatedTime:   types.Int64Value(int64(recordBService.CreatedTime)),
		DeletedBy:     types.StringValue(recordBService.DeletedBy),
		DeletedTime:   types.Int64Value(int64(recordBService.DeletedTime)),
		DiskTotal:     types.Int64Value(int64(recordBService.DiskTotal)),
		GID:           types.Int64Value(int64(recordBService.GID)),
		GUID:          types.Int64Value(int64(recordBService.GUID)),
		Milestones:    types.Int64Value(int64(recordBService.Milestones)),
		ParentSrvID:   types.Int64Value(int64(recordBService.ParentSrvID)),
		ParentSrvType: types.StringValue(recordBService.ParentSrvType),
		RAMTotal:      types.Int64Value(int64(recordBService.RAMTotal)),
		RGName:        types.StringValue(recordBService.RGName),
		Status:        types.StringValue(recordBService.Status),
		TechStatus:    types.StringValue(recordBService.TechStatus),
		UpdatedBy:     types.StringValue(recordBService.UpdatedBy),
		UpdatedTime:   types.Int64Value(int64(recordBService.UpdatedTime)),
		UserManaged:   types.BoolValue(recordBService.UserManaged),
		ID:            types.StringValue(strconv.Itoa(int(serviceId))),
	}

	tflog.Info(ctx, "flattens.BServiceResource: after flatten", map[string]any{"service_id": state.ID.ValueString()})
	tflog.Info(ctx, "End flattens.BServiceResource", map[string]any{"service_id": state.ID.ValueString()})
	return nil
}

func flattenComputes(ctx context.Context, items bservice.ListComputes) types.List {
	tflog.Info(ctx, "Start flattenComputes")
	tempSlice := make([]types.Object, 0, len(items))
	for _, v := range items {
		temp := models.ItemComputeResourceModel{
			AccountID:     types.Int64Value(int64(v.AccountID)),
			Architecture:  types.StringValue(v.Architecture),
			CompGroupID:   types.Int64Value(int64(v.CompGroupID)),
			CompGroupName: types.StringValue(v.CompGroupName),
			CompGroupRole: types.StringValue(v.CompGroupRole),
			ID:            types.Int64Value(int64(v.ID)),
			Name:          types.StringValue(v.Name),
			RGID:          types.Int64Value(int64(v.RGID)),
			StackID:       types.Int64Value(int64(v.StackID)),
			Status:        types.StringValue(v.Status),
			TechStatus:    types.StringValue(v.TechStatus),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemComputeResource, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenComputes struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemComputeResource}, tempSlice)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenComputes", diags))
	}

	tflog.Info(ctx, "End flattenComputes")
	return res
}

func flattenGroups(ctx context.Context, items bservice.ListGroups) types.List {
	tflog.Info(ctx, "Start flattenGroups")
	tempSlice := make([]types.Object, 0, len(items))
	for _, v := range items {
		temp := models.ItemGroupResourceModel{
			Computes:    types.Int64Value(int64(v.Computes)),
			Consistency: types.BoolValue(v.Consistency),
			ID:          types.Int64Value(int64(v.ID)),
			Name:        types.StringValue(v.Name),
			Status:      types.StringValue(v.Status),
			TechStatus:  types.StringValue(v.TechStatus),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemGroupResource, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenGroups struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemGroupResource}, tempSlice)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenGroups", diags))
	}

	tflog.Info(ctx, "End flattenGroups")
	return res
}

func flattenSnapshot(ctx context.Context, snapshots types.Set, items bservice.ListSnapshots) types.Set {
	tflog.Info(ctx, "Start flattenSnapshot")
	tempSlice := make([]types.Object, 0, len(items))
	for _, v := range items {
		temp := models.ItemSnapshotResourceModel{
			GUID:      types.StringValue(v.GUID),
			Label:     types.StringValue(v.Label),
			Rollback:  flattenSnapshotRollback(ctx, snapshots, v),
			Timestamp: types.Int64Value(int64(v.Timestamp)),
			Valid:     types.BoolValue(v.Valid),
		}
		obj, diags := types.ObjectValueFrom(ctx, models.ItemSnapshotResource, temp)
		if diags != nil {
			tflog.Error(ctx, fmt.Sprint("Error flattenSnapshot struct to obj", diags))
		}
		tempSlice = append(tempSlice, obj)
	}

	res, diags := types.SetValueFrom(ctx, types.ObjectType{AttrTypes: models.ItemSnapshotResource}, tempSlice)
	if diags != nil {
		tflog.Error(ctx, fmt.Sprint("Error flattenSnapshot", diags))
	}

	tflog.Info(ctx, "End flattenSnapshot")
	return res
}

func flattenSnapshotRollback(ctx context.Context, snapshots types.Set, item bservice.ItemSnapshot) types.Bool {
	tflog.Info(ctx, "Start flattenSnapshotRollback")
	snapshotsList := snapshots.Elements()
	for _, snapshot := range snapshotsList {
		snapshotMap := snapshot.(types.Object).Attributes()
		if snapshotMap["label"].(types.String).ValueString() == item.Label {
			return types.BoolValue(snapshotMap["rollback"].(types.Bool).ValueBool())
		}
	}
	tflog.Info(ctx, "End flattenSnapshotRollback")
	return types.BoolValue(false)
}
