package disable

import (
	"context"
	"fmt"
	"net/http"

	backupRepo "github.com/tidbcloud/dataflow-service/app/repo/backup"
	crrfRepo "github.com/tidbcloud/dataflow-service/app/repo/crrf"
	"github.com/tidbcloud/dataflow-service/app/workflow"
	"github.com/tidbcloud/dataflow-service/app/workflow/action"
	"github.com/tidbcloud/dataflow-service/app/workflow/action/crrf/common"
	regionalapi "github.com/tidbcloud/dataflow-service/pkg/regional-api"
	"go.uber.org/zap"
)

type disableCRRFDaemonParams struct {
	ClusterID uint64 `json:"cluster_id"`
}

type disableCRRFDaemonAction struct {
	backupSettingRepo backupRepo.BackupSettingRepo
	logBackupRepo     backupRepo.LogBackupRepo
	crrfSettingRepo   crrfRepo.CRRFSettingRepo
	crrfFacade        regionalapi.CRRFFacade
}

// NewDisableCRRFDaemonAction returns a Wrapper that calls DisableCRRF on cluster-server
// in Run and polls GetCRRF until the daemon is DISABLED or gone in RunAfter.
func NewDisableCRRFDaemonAction(
	backupSettingRepo backupRepo.BackupSettingRepo,
	logBackupRepo backupRepo.LogBackupRepo,
	crrfSettingRepo crrfRepo.CRRFSettingRepo,
	crrfFacade regionalapi.CRRFFacade,
) *action.Wrapper {
	return action.MustNewActionWrapper(
		workflow.ActionCRRFDisableDaemon,
		&disableCRRFDaemonAction{
			backupSettingRepo: backupSettingRepo,
			logBackupRepo:     logBackupRepo,
			crrfSettingRepo:   crrfSettingRepo,
			crrfFacade:        crrfFacade,
		},
	)
}

func (a *disableCRRFDaemonAction) Params() interface{} {
	return &disableCRRFDaemonParams{}
}

// Run calls DisableCRRF on cluster-server to tear down the CRRF daemon.
// Idempotent: 404 or DISABLED/DESTROYING phase means the daemon is already gone/going.
func (a *disableCRRFDaemonAction) Run(ctx context.Context, logger *zap.Logger, params interface{}) error {
	p := params.(*disableCRRFDaemonParams)

	actCtx, err := common.LoadCRRFActionContext(
		ctx, p.ClusterID,
		a.backupSettingRepo, a.crrfSettingRepo, a.logBackupRepo,
	)
	if err != nil {
		return err
	}

	// Check current state first to avoid redundant calls.
	existing, bizErr := a.crrfFacade.GetCRRF(ctx, actCtx.MasterRegion, &regionalapi.GetCRRFRequest{
		ClusterId:     fmt.Sprint(p.ClusterID),
		LogBackupName: actCtx.LogBackup.CRName(),
	})
	if bizErr != nil {
		if bizErr.HttpStatus() == http.StatusNotFound {
			logger.Info("CRRF daemon not found, skipping DisableCRRF call",
				zap.Uint64("clusterID", p.ClusterID))
			return nil
		}
		return bizErr
	}
	if existing.Phase == regionalapi.CRRFPhaseDisabled || existing.Phase == regionalapi.CRRFPhaseDestroying {
		logger.Info("CRRF daemon already being torn down, skipping DisableCRRF call",
			zap.Uint64("clusterID", p.ClusterID),
			zap.String("phase", string(existing.Phase)))
		return nil
	}

	req := &regionalapi.DisableCRRFRequest{
		ClusterId:     fmt.Sprint(p.ClusterID),
		LogBackupName: actCtx.LogBackup.CRName(),
		CrrfId:        regionalapi.CRRFDaemonID(uint64(actCtx.CRRFSetting.ID)),
	}
	if _, bizErr := a.crrfFacade.DisableCRRF(ctx, actCtx.MasterRegion, req); bizErr != nil {
		logger.Warn("DisableCRRF call failed", zap.Error(bizErr))
		return bizErr
	}

	logger.Info("DisableCRRF call succeeded",
		zap.Uint64("clusterID", p.ClusterID),
		zap.String("logBackupName", actCtx.LogBackup.CRName()),
		zap.String("crrfId", req.CrrfId))
	return nil
}

// RunAfter polls until the CRRF daemon is DISABLED or has disappeared (404).
func (a *disableCRRFDaemonAction) RunAfter(ctx context.Context, logger *zap.Logger, params interface{}) error {
	p := params.(*disableCRRFDaemonParams)

	actCtx, err := common.LoadCRRFActionContext(
		ctx, p.ClusterID,
		a.backupSettingRepo, a.crrfSettingRepo, a.logBackupRepo,
	)
	if err != nil {
		return err
	}

	info, bizErr := a.crrfFacade.GetCRRF(ctx, actCtx.MasterRegion, &regionalapi.GetCRRFRequest{
		ClusterId:     fmt.Sprint(p.ClusterID),
		LogBackupName: actCtx.LogBackup.CRName(),
	})
	if bizErr != nil {
		if bizErr.HttpStatus() == http.StatusNotFound {
			logger.Info("CRRF daemon destroyed (404), marking success",
				zap.Uint64("clusterID", p.ClusterID))
			return nil
		}
		return bizErr
	}

	switch info.Phase {
	case regionalapi.CRRFPhaseDisabled:
		logger.Info("CRRF daemon is DISABLED",
			zap.Uint64("clusterID", p.ClusterID))
		return nil
	case regionalapi.CRRFPhaseFailed:
		logger.Error("CRRF daemon entered FAILED phase during disable",
			zap.Uint64("clusterID", p.ClusterID),
			zap.String("message", info.Message))
		return action.StopRetry
	default:
		return fmt.Errorf("CRRF daemon not yet DISABLED for cluster %d (phase: %s)", p.ClusterID, info.Phase)
	}
}
