package gke

import (
	"context"
	"fmt"

	"github.com/crossplane-contrib/provider-gcp/apis/compute/v1alpha1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/utils/pointer"

	"github.com/tidbcloud/eks-provider/controllers/network/task"
	"github.com/tidbcloud/infra-provider/pkg/applier"
	"github.com/tidbcloud/infra-provider/pkg/applier/mutatefuncs"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/namer"
)

type PSCFirewall struct {
	*task.BaseTask
}

func NewPSCFirewall(ctx *task.Context) *PSCFirewall {
	return &PSCFirewall{
		BaseTask: &task.BaseTask{Context: ctx},
	}
}

func (t *PSCFirewall) Name() string {
	return "PSCFirewall"
}

func (t *PSCFirewall) Create(ctx context.Context) deploy.Result {
	firewall := &v1alpha1.Firewall{
		ObjectMeta: metav1.ObjectMeta{Name: t.firewallName()},
		Spec: v1alpha1.FirewallSpec{
			ForProvider: v1alpha1.FirewallParameters{
				Direction: pointer.String("INGRESS"),
				Allowed: []*v1alpha1.FirewallAllowed{
					{
						IPProtocol: "tcp",
					},
				},
				SourceRanges: []string{t.GKECIDRDispatcher.PSCNATIPv4CIDRBlock()},
				NetworkRef: &xpv1.Reference{
					Name: namer.UniqueIdentifier(t.Network.ObjectMeta),
				},
				// Not specify targetTags, so all instances in the network will be applied
			},
			ResourceSpec: xpv1.ResourceSpec{
				ProviderConfigReference: &xpv1.Reference{
					Name: namer.UniqueIdentifier(t.Network.ObjectMeta),
				},
			},
		},
	}
	applier := applier.NewApplier(t.Local, mutatefuncs.MutateFirewall)
	if _, err := applier.Expected(firewall).Apply(ctx); err != nil {
		return deploy.Error(err)
	}
	return deploy.Done()
}

func (t *PSCFirewall) firewallName() string {
	return fmt.Sprintf("%s-gke-%s-psc-ingress", t.Network.GetNamespace(), t.Network.Spec.Region)
}

func (t *PSCFirewall) PostCreate(ctx context.Context) deploy.Result {
	firewall := &v1alpha1.Firewall{}
	key := types.NamespacedName{
		Name: t.firewallName(),
	}
	err := t.Local.Get(ctx, key, firewall)
	if err != nil {
		return deploy.Error(err)
	}

	for _, f := range firewall.Status.Conditions {
		if f.Type == xpv1.TypeReady && f.Status != corev1.ConditionTrue {
			return deploy.Error(fmt.Errorf("PSC firewall not ready: %s", f.Message))
		}
	}
	return deploy.Done()
}

func (t *PSCFirewall) Delete(ctx context.Context) deploy.Result {
	firewall := &v1alpha1.Firewall{
		ObjectMeta: metav1.ObjectMeta{
			Name: t.firewallName(),
		},
	}
	err := k8s.DeleteObject(ctx, t.Local, firewall)
	return deploy.ErrorOrDone(err)
}

// PostDelete implements Task interface.
func (t *PSCFirewall) PostDelete(ctx context.Context) deploy.Result {
	return deploy.Done()
}
