package alert_push

import (
	"strconv"
	"strings"

	"github.com/1Panel-dev/1Panel/agent/app/dto"
	"github.com/1Panel-dev/1Panel/agent/app/model"
	"github.com/1Panel-dev/1Panel/agent/app/repo"
	"github.com/1Panel-dev/1Panel/agent/constant"
	"github.com/1Panel-dev/1Panel/agent/global"
	alertUtil "github.com/1Panel-dev/1Panel/agent/utils/alert"
	"github.com/1Panel-dev/1Panel/agent/utils/xpack"
	"github.com/jinzhu/copier"
)

func PushAlert(pushAlert dto.PushAlert) error {
	if !alertUtil.CheckSendTimeRange(alertUtil.GetCronJobType(pushAlert.AlertType)) {
		return nil
	}

	alertRepo := repo.NewIAlertRepo()
	alertInfo, err := alertRepo.Get(alertRepo.WithByType(pushAlert.AlertType), alertRepo.WithByProject(strconv.Itoa(int(pushAlert.EntryID))), repo.WithByStatus(constant.AlertEnable))
	if err != nil {
		return err
	}
	var alert dto.AlertDTO
	_ = copier.Copy(&alert, &alertInfo)

	methods := strings.Split(alert.Method, ",")
	for _, m := range methods {
		m = strings.TrimSpace(m)
		if configId, err := strconv.ParseUint(m, 10, 64); err == nil {
			pushByConfigId(alertRepo, alert, pushAlert, uint(configId))
		} else {
			pushByLegacyMethod(alertRepo, alert, pushAlert, m)
		}
	}
	return nil
}

func pushByConfigId(alertRepo repo.IAlertRepo, alert dto.AlertDTO, pushAlert dto.PushAlert, configId uint) {
	config, err := alertRepo.GetConfigById(configId)
	if err != nil {
		global.LOG.Errorf("alert config not found for id %d: %v", configId, err)
		return
	}
	sendAlert(alertRepo, alert, pushAlert, config)
}

func pushByLegacyMethod(alertRepo repo.IAlertRepo, alert dto.AlertDTO, pushAlert dto.PushAlert, method string) {
	typeMap := map[string]string{
		"mail":        constant.Email,
		constant.Bark: constant.Bark,
		constant.SMS:  constant.SMS,
	}
	configType := method
	if mapped, ok := typeMap[method]; ok {
		configType = mapped
	}
	config, err := alertRepo.GetConfig(alertRepo.WithByType(configType))
	if err != nil {
		return
	}
	sendAlert(alertRepo, alert, pushAlert, config)
}

func sendAlert(alertRepo repo.IAlertRepo, alert dto.AlertDTO, pushAlert dto.PushAlert, config model.AlertConfig) {
	if !alertUtil.IsAlertConfigEnabled(config) {
		return
	}
	methodStr := strconv.Itoa(int(config.ID))
	switch config.Type {
	case constant.SMS:
		if !alertUtil.CheckSMSSendLimit(config, methodStr) {
			return
		}
		todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)
		if err != nil || alert.SendCount <= todayCount {
			return
		}
		create := dto.AlertLogCreate{
			Type:    alertUtil.GetCronJobType(alert.Type),
			AlertId: alert.ID,
			Count:   todayCount + 1,
			Method:  methodStr,
		}
		err = xpack.AlertProvider.CreateTaskScanSMSAlertLog(alert, alert.Type, create, pushAlert, config, methodStr)
		if err != nil {
			global.LOG.Errorf("%s alert sms push failed: %v", alert.Type, err)
			return
		}
		alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)

	case constant.Email:
		todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)
		if err != nil || alert.SendCount <= todayCount {
			return
		}
		create := dto.AlertLogCreate{
			Type:    alertUtil.GetCronJobType(alert.Type),
			AlertId: alert.ID,
			Count:   todayCount + 1,
			Method:  methodStr,
		}
		transport := xpack.MultiNodeProvider.LoadRequestTransport()
		agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
		err = alertUtil.CreateTaskScanEmailAlertLog(alert, create, pushAlert, constant.Email, transport, agentInfo, config)
		if err != nil {
			global.LOG.Errorf("%s alert email push failed: %v", alert.Type, err)
			return
		}
		alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)

	case constant.Bark:
		todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)
		if err != nil || alert.SendCount <= todayCount {
			return
		}
		create := dto.AlertLogCreate{
			Type:    alertUtil.GetCronJobType(alert.Type),
			AlertId: alert.ID,
			Count:   todayCount + 1,
			Method:  methodStr,
		}
		transport := xpack.MultiNodeProvider.LoadRequestTransport()
		agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
		params := alertUtil.CreateAlertParams(alertUtil.GetCronJobTypeName(pushAlert.Param))
		alertDetail := alertUtil.ProcessAlertDetail(alert, pushAlert.TaskName, params, constant.Bark)
		alertRule := alertUtil.ProcessAlertRule(alert)
		create.AlertRule = alertRule
		create.AlertDetail = alertDetail
		err = alertUtil.CreateBarkAlertLog(create, alert, params, transport, agentInfo, config)
		if err != nil {
			global.LOG.Errorf("%s alert bark push failed: %v", alert.Type, err)
			return
		}
		alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)

	case constant.WeCom, constant.DingTalk, constant.FeiShu:
		todayCount, _, err := alertRepo.LoadTaskCount(alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)
		if err != nil || alert.SendCount <= todayCount {
			return
		}
		create := dto.AlertLogCreate{
			Type:    alertUtil.GetCronJobType(alert.Type),
			AlertId: alert.ID,
			Count:   todayCount + 1,
			Method:  methodStr,
		}
		transport := xpack.MultiNodeProvider.LoadRequestTransport()
		agentInfo, _ := xpack.MultiNodeProvider.GetAgentInfo()
		err = xpack.AlertProvider.CreateTaskScanWebhookAlertLog(alert, alert.Type, create, pushAlert, config, transport, agentInfo)
		if err != nil {
			global.LOG.Errorf("%s alert %s webhook push failed: %v", alert.Type, methodStr, err)
			return
		}
		alertUtil.CreateNewAlertTask(strconv.Itoa(int(pushAlert.EntryID)), alertUtil.GetCronJobType(alert.Type), strconv.Itoa(int(pushAlert.EntryID)), methodStr)
	}
}
