// Copyright (c) 2025 Aiven, Helsinki, Finland. https://aiven.io/
// Code generated by user config generator. DO NOT EDIT.

package access

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"

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

	"github.com/aiven/terraform-provider-aiven/internal/plugin/util"
)

const aivenName = "aiven_governance_access"

type tfModel struct {
	ID               types.String `tfsdk:"id"`
	AccessData       types.List   `tfsdk:"access_data"`
	AccessName       types.String `tfsdk:"access_name"`
	AccessType       types.String `tfsdk:"access_type"`
	OrganizationID   types.String `tfsdk:"organization_id"`
	OwnerUserGroupID types.String `tfsdk:"owner_user_group_id"`
	SusbcriptionID   types.String `tfsdk:"susbcription_id"`
}

func (tf *tfModel) SetID(vOrganizationID string, vSusbcriptionID string) {
	tf.OrganizationID = types.StringValue(vOrganizationID)
	tf.SusbcriptionID = types.StringValue(vSusbcriptionID)
	tf.ID = types.StringValue(filepath.Join(vOrganizationID, vSusbcriptionID))
}

type tfModelAccessData struct {
	Acls        types.Set    `tfsdk:"acls"`
	Project     types.String `tfsdk:"project"`
	ServiceName types.String `tfsdk:"service_name"`
	Username    types.String `tfsdk:"username"`
}

type tfModelAccessDataAcls struct {
	Host           types.String `tfsdk:"host"`
	ID             types.String `tfsdk:"id"`
	Operation      types.String `tfsdk:"operation"`
	PatternType    types.String `tfsdk:"pattern_type"`
	PermissionType types.String `tfsdk:"permission_type"`
	Principal      types.String `tfsdk:"principal"`
	ResourceName   types.String `tfsdk:"resource_name"`
	ResourceType   types.String `tfsdk:"resource_type"`
}

type apiModel struct {
	AccessData       *apiModelAccessData `json:"access_data,omitempty"`
	AccessName       *string             `json:"access_name,omitempty"`
	AccessType       *string             `json:"access_type,omitempty"`
	OrganizationID   *string             `json:"organization_id,omitempty"`
	OwnerUserGroupID *string             `json:"owner_user_group_id,omitempty"`
	SusbcriptionID   *string             `json:"access_id,omitempty"`
}

type apiModelAccessData struct {
	Acls        *[]*apiModelAccessDataAcls `json:"acls,omitempty"`
	Project     *string                    `json:"project_name,omitempty"`
	ServiceName *string                    `json:"service_name,omitempty"`
	Username    *string                    `json:"username,omitempty"`
}

type apiModelAccessDataAcls struct {
	Host           *string `json:"host,omitempty"`
	ID             *string `json:"id,omitempty"`
	Operation      *string `json:"operation,omitempty"`
	PatternType    *string `json:"pattern_type,omitempty"`
	PermissionType *string `json:"permission_type,omitempty"`
	Principal      *string `json:"principal,omitempty"`
	ResourceName   *string `json:"resource_name,omitempty"`
	ResourceType   *string `json:"resource_type,omitempty"`
}

// composeID the ID attribute fields, i.e.:
// terraform import aiven_governance_access.foo ORGANIZATION_ID/SUSBCRIPTION_ID
func composeID() []string {
	return []string{"organization_id", "susbcription_id"}
}

// expandData turns TF object into Request
func expandData[R any](ctx context.Context, plan, state *tfModel, req *R, modifiers ...util.MapModifier[apiModel]) diag.Diagnostics {
	api := new(apiModel)
	if !plan.AccessData.IsNull() || state != nil && !state.AccessData.IsNull() {
		vAccessData, diags := util.ExpandSingleNested(ctx, expandAccessData, plan.AccessData)
		if diags.HasError() {
			return diags
		}
		api.AccessData = vAccessData
	}
	if !plan.AccessName.IsNull() || state != nil && !state.AccessName.IsNull() {
		vAccessName := plan.AccessName.ValueString()
		api.AccessName = &vAccessName
	}
	if !plan.AccessType.IsNull() || state != nil && !state.AccessType.IsNull() {
		vAccessType := plan.AccessType.ValueString()
		api.AccessType = &vAccessType
	}
	if !plan.OrganizationID.IsNull() || state != nil && !state.OrganizationID.IsNull() {
		vOrganizationID := plan.OrganizationID.ValueString()
		api.OrganizationID = &vOrganizationID
	}
	if !plan.OwnerUserGroupID.IsNull() || state != nil && !state.OwnerUserGroupID.IsNull() {
		vOwnerUserGroupID := plan.OwnerUserGroupID.ValueString()
		api.OwnerUserGroupID = &vOwnerUserGroupID
	}
	err := util.Unmarshal(api, req, modifiers...)
	if err != nil {
		var diags diag.Diagnostics
		diags.AddError("Unmarshal error", fmt.Sprintf("Failed to unmarshal dtoModel to Request: %s", err.Error()))
		return diags
	}
	return nil
}

func expandAccessData(ctx context.Context, plan *tfModelAccessData) (*apiModelAccessData, diag.Diagnostics) {
	api := new(apiModelAccessData)
	if !plan.Acls.IsNull() {
		vAcls, diags := util.ExpandSetNested(ctx, expandAccessDataAcls, plan.Acls)
		if diags.HasError() {
			return nil, diags
		}
		api.Acls = &vAcls
	}
	if !plan.Project.IsNull() {
		vProject := plan.Project.ValueString()
		api.Project = &vProject
	}
	if !plan.ServiceName.IsNull() {
		vServiceName := plan.ServiceName.ValueString()
		api.ServiceName = &vServiceName
	}
	if !plan.Username.IsNull() && !plan.Username.IsUnknown() {
		vUsername := plan.Username.ValueString()
		api.Username = &vUsername
	}
	return api, nil
}

func expandAccessDataAcls(ctx context.Context, plan *tfModelAccessDataAcls) (*apiModelAccessDataAcls, diag.Diagnostics) {
	api := new(apiModelAccessDataAcls)
	if !plan.Host.IsNull() && !plan.Host.IsUnknown() {
		vHost := plan.Host.ValueString()
		api.Host = &vHost
	}
	if !plan.Operation.IsNull() {
		vOperation := plan.Operation.ValueString()
		api.Operation = &vOperation
	}
	if !plan.PermissionType.IsNull() {
		vPermissionType := plan.PermissionType.ValueString()
		api.PermissionType = &vPermissionType
	}
	if !plan.ResourceName.IsNull() {
		vResourceName := plan.ResourceName.ValueString()
		api.ResourceName = &vResourceName
	}
	if !plan.ResourceType.IsNull() {
		vResourceType := plan.ResourceType.ValueString()
		api.ResourceType = &vResourceType
	}
	return api, nil
}

// flattenData turns Response into TF object
func flattenData[R any](ctx context.Context, state *tfModel, rsp *R, modifiers ...util.MapModifier[R]) diag.Diagnostics {
	api := new(apiModel)
	err := util.Unmarshal(rsp, api, modifiers...)
	if err != nil {
		var diags diag.Diagnostics
		diags.AddError("Unmarshal error", fmt.Sprintf("Failed to unmarshal Response to dtoModel: %s", err.Error()))
		return diags
	}
	if api.AccessData != nil {
		vAccessData, diags := util.FlattenSingleNested(ctx, flattenAccessData, api.AccessData, attrsAccessData())
		if diags.HasError() {
			return diags
		}
		state.AccessData = vAccessData
	}
	if api.AccessName != nil && (*api.AccessName != "" || !state.AccessName.IsNull()) {
		state.AccessName = types.StringPointerValue(api.AccessName)
	}
	if api.AccessType != nil && (*api.AccessType != "" || !state.AccessType.IsNull()) {
		state.AccessType = types.StringPointerValue(api.AccessType)
	}
	if api.OrganizationID != nil && (*api.OrganizationID != "" || !state.OrganizationID.IsNull()) {
		state.OrganizationID = types.StringPointerValue(api.OrganizationID)
	}
	if api.OwnerUserGroupID != nil && (*api.OwnerUserGroupID != "" || !state.OwnerUserGroupID.IsNull()) {
		state.OwnerUserGroupID = types.StringPointerValue(api.OwnerUserGroupID)
	}
	if api.SusbcriptionID != nil && (*api.SusbcriptionID != "" || !state.SusbcriptionID.IsNull()) {
		state.SusbcriptionID = types.StringPointerValue(api.SusbcriptionID)
	}
	// Response may not contain ID fields.
	// In that case, `terraform import` won't be able to set them. Gets values from the ID.
	if state.ID.ValueString() != "" {
		var parts [2]string
		for i, v := range strings.SplitN(state.ID.ValueString(), "/", 2) {
			parts[i] = v
		}
		if state.OrganizationID.ValueString() == "" {
			state.OrganizationID = types.StringValue(parts[0])
		}
		if state.SusbcriptionID.ValueString() == "" {
			state.SusbcriptionID = types.StringValue(parts[1])
		}
	}
	state.SetID(state.OrganizationID.ValueString(), state.SusbcriptionID.ValueString())
	return nil
}

func flattenAccessData(ctx context.Context, api *apiModelAccessData) (*tfModelAccessData, diag.Diagnostics) {
	state := new(tfModelAccessData)
	if api.Acls != nil {
		vAcls, diags := util.FlattenSetNested(ctx, flattenAccessDataAcls, *api.Acls, attrsAccessDataAcls())
		if diags.HasError() {
			return nil, diags
		}
		state.Acls = vAcls
	}
	if api.Project != nil {
		state.Project = types.StringPointerValue(api.Project)
	}
	if api.ServiceName != nil {
		state.ServiceName = types.StringPointerValue(api.ServiceName)
	}
	if api.Username != nil {
		state.Username = types.StringPointerValue(api.Username)
	}
	return state, nil
}

func flattenAccessDataAcls(ctx context.Context, api *apiModelAccessDataAcls) (*tfModelAccessDataAcls, diag.Diagnostics) {
	state := new(tfModelAccessDataAcls)
	if api.Host != nil {
		state.Host = types.StringPointerValue(api.Host)
	}
	if api.ID != nil {
		state.ID = types.StringPointerValue(api.ID)
	}
	if api.Operation != nil {
		state.Operation = types.StringPointerValue(api.Operation)
	}
	if api.PatternType != nil {
		state.PatternType = types.StringPointerValue(api.PatternType)
	}
	if api.PermissionType != nil {
		state.PermissionType = types.StringPointerValue(api.PermissionType)
	}
	if api.Principal != nil {
		state.Principal = types.StringPointerValue(api.Principal)
	}
	if api.ResourceName != nil {
		state.ResourceName = types.StringPointerValue(api.ResourceName)
	}
	if api.ResourceType != nil {
		state.ResourceType = types.StringPointerValue(api.ResourceType)
	}
	return state, nil
}

func attrsAccessData() types.ObjectType {
	return types.ObjectType{AttrTypes: map[string]attr.Type{
		"acls":         types.SetType{ElemType: attrsAccessDataAcls()},
		"project":      types.StringType,
		"service_name": types.StringType,
		"username":     types.StringType,
	}}
}

func attrsAccessDataAcls() types.ObjectType {
	return types.ObjectType{AttrTypes: map[string]attr.Type{
		"host":            types.StringType,
		"id":              types.StringType,
		"operation":       types.StringType,
		"pattern_type":    types.StringType,
		"permission_type": types.StringType,
		"principal":       types.StringType,
		"resource_name":   types.StringType,
		"resource_type":   types.StringType,
	}}
}
