package utils

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

	maintent "github.com/tidbcloud/devops/entity/maintenance"

	k8sent "github.com/tidbcloud/devops/entity/k8supgrade"
	cmxapi "github.com/tidbcloud/devops/pkg/service/clustermaintenance/api"
)

const (
	upgradeDescription = "Upgrade the underlying infrastructure"
)

func MakeCreateMaintenanceParam(info *k8sent.K8sInfo, deadline int64) (cmxapi.CreateMaintenanceParam, error) {
	pid, err := strconv.ParseUint(info.ProjectID, 10, 64)
	if err != nil {
		return cmxapi.CreateMaintenanceParam{}, fmt.Errorf("failed to parse project ID: %w", err)
	}

	param := &cmxapi.CreateMaintenanceParam{
		ProjectID:   pid,
		Description: upgradeDescription,
		Deadline:    time.Unix(deadline, 0),
		ClusterIDs:  make([]uint64, 0, info.NumberOfTiDBClusters),
	}

	for _, cluster := range info.TiDBClusters {
		cid, err := strconv.ParseUint(cluster.ID, 10, 64)
		if err != nil {
			return cmxapi.CreateMaintenanceParam{}, fmt.Errorf("failed to parse cluster ID: %w", err)
		}
		param.ClusterIDs = append(param.ClusterIDs, cid)
	}

	return *param, nil
}

func CloseMaintenanceWindowByID(ctx context.Context, svc cmxapi.ServiceAPI, mwID *uint64) error {
	if mwID == nil {
		return nil
	}

	mw, err := svc.GetMaintenance(ctx, *mwID, cmxapi.GetMaintenanceParam{
		WithExecutions: true,
	})
	if err != nil {
		return fmt.Errorf("fail to get maintenance window: %w", err)
	}

	return CloseMaintenanceWindow(ctx, svc, mw)
}

func CloseMaintenanceWindow(ctx context.Context, svc cmxapi.ServiceAPI, mw *maintent.ClusterMaintenance) error {
	if mw == nil || mw.Task.Status == maintent.ClusterMaintenanceTaskStatusClosed {
		return nil
	}

	for _, exec := range mw.Task.Executions {
		if exec.Status == maintent.ClusterMaintenanceExecutionStatusPending {
			if err := svc.CancelMaintenanceExecution(ctx, mw.ID, exec.ID); err != nil {
				return fmt.Errorf("failed to cancel the  execution %d of the maintenance window %d: %w", exec.ID, mw.ID, err)
			}
		}
	}

	if err := svc.CloseMaintenance(ctx, mw.ID); err != nil {
		return fmt.Errorf("failed to close maintenance window %d: %w", mw.ID, err)
	}
	return nil
}

func findExecutionByClusterID(mw *maintent.ClusterMaintenance, clusterID uint64) (*maintent.ClusterMaintenanceExecution, bool) {
	for i := range mw.Task.Executions {
		if mw.Task.Executions[i].ClusterID == clusterID {
			return &mw.Task.Executions[i], true
		}
	}
	return nil, false
}

func StartMaintenanceExecutionForCluster(ctx context.Context, svc cmxapi.ServiceAPI, mw *maintent.ClusterMaintenance, clusterID uint64) error {
	exec, ok := findExecutionByClusterID(mw, clusterID)
	if !ok {
		return fmt.Errorf("maintenance execution not found for cluster %d", clusterID)
	}
	if exec.Status == maintent.ClusterMaintenanceExecutionStatusRunning {
		return nil
	}
	if exec.Status != maintent.ClusterMaintenanceExecutionStatusPending {
		return fmt.Errorf("maintenance execution %d for cluster %d is in status %s", exec.ID, clusterID, exec.Status)
	}
	if err := svc.StartMaintenanceExecution(ctx, mw.ID, exec.ID); err != nil {
		return fmt.Errorf("failed to start maintenance execution %d: %w", exec.ID, err)
	}
	return nil
}

func FinishMaintenanceExecutionForCluster(ctx context.Context, svc cmxapi.ServiceAPI, mw *maintent.ClusterMaintenance, clusterID uint64) error {
	exec, ok := findExecutionByClusterID(mw, clusterID)
	if !ok {
		return fmt.Errorf("maintenance execution not found for cluster %d", clusterID)
	}
	if exec.Status == maintent.ClusterMaintenanceExecutionStatusDone {
		return nil
	}
	if exec.Status != maintent.ClusterMaintenanceExecutionStatusRunning {
		return fmt.Errorf("maintenance execution %d for cluster %d is in status %s", exec.ID, clusterID, exec.Status)
	}
	if err := svc.FinishMaintenanceExecution(ctx, mw.ID, exec.ID); err != nil {
		return fmt.Errorf("failed to finish maintenance execution %d: %w", exec.ID, err)
	}
	return nil
}
