package common

import (
	"encoding/json"
	"fmt"

	"github.com/overdone/jsonmatch"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/bucket/task"
)

func IsBucketPolicyNeeded(dataPlane *v1alpha1.DataPlane, bucket *v1alpha1.Bucket) bool {
	return IsDefaultStatementInBucketPolicyNeeded(dataPlane) || len(bucket.Spec.GrantedDataplaneIds) != 0
}

func IsDefaultStatementInBucketPolicyNeeded(dataPlane *v1alpha1.DataPlane) bool {
	return dataPlane != nil && dataPlane.DeletionTimestamp == nil
}

func ConvertStatementStatus2DataplanePermissionStatus(inSpec, inStatus bool) task.BucketDataplanePermissionStatus {
	if inSpec && inStatus {
		return task.BucketDataplanePermissionStatusSynced
	}
	if inSpec {
		return task.BucketDataplanePermissionStatusAccepted
	}
	return task.BucketDataplanePermissionStatusPending
}

func GetBucketPolicyStatementStatus(policyInSpec, policyInStatus *string, theStatement interface{}) (inSpec, inStatus bool, err error) {
	statementJson, err := json.Marshal(theStatement)
	if err != nil {
		return false, false, fmt.Errorf("failed to marshal statement: %w", err)
	}

	foundInSpec, err := isDataplaneInPolicy(policyInSpec, string(statementJson))
	if err != nil {
		return false, false, fmt.Errorf("failed to check if the statement is in policy spec, error: %w", err)
	}
	if !foundInSpec {
		return false, false, nil
	}

	foundInStatus, err := isDataplaneInPolicy(policyInStatus, string(statementJson))
	if err != nil {
		return false, false, fmt.Errorf("failed to check if the statement is in policy status, error: %w", err)
	}
	if !foundInStatus {
		return true, false, nil
	}

	return true, true, nil
}

func isDataplaneInPolicy(policy *string, theStatementJson string) (bool, error) {
	if policy == nil {
		return false, nil
	}

	var pi map[string]interface{}
	if err := json.Unmarshal([]byte(*policy), &pi); err != nil {
		return false, fmt.Errorf("failed to unmarshal policy: %w", err)
	}

	statementField, ok := pi["Statement"]
	if !ok {
		return false, fmt.Errorf("statement field not found in policy")
	}

	statements, ok := statementField.([]interface{})
	if !ok {
		return false, fmt.Errorf("statement field is not an array")
	}

	var theStatement interface{}
	if err := json.Unmarshal([]byte(theStatementJson), &theStatement); err != nil {
		return false, fmt.Errorf("failed to unmarshal the statement: %w", err)
	}

	comparator := jsonmatch.NewComparator(jsonmatch.CompareOptions{IgnoreArrayOrder: true})
	for _, statement := range statements {
		match, _ := comparator.Compare(theStatement, statement)
		if match {
			return true, nil
		}
	}

	return false, nil
}
