package adminpush

import (
	"context"
	"encoding/json"
	"errors"
	"strings"

	"hfb_sys/backend/internal/integrations/push"
)

var (
	ErrDependencyUnavailable = errors.New("dependency unavailable")
	ErrChannelNotFound       = errors.New("push channel not found")
	ErrRuleNotFound          = errors.New("push rule not found")
	ErrInvalidChannel        = errors.New("invalid channel type or config")
)

type Service struct {
	repo *Repository
}

func NewService(repo *Repository) *Service {
	return &Service{repo: repo}
}

func (s *Service) ListChannels(ctx context.Context) ([]ChannelDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.ListChannels(ctx)
}

func (s *Service) CreateChannel(ctx context.Context, req CreateChannelRequest) (*ChannelDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if req.Name == "" || req.Type == "" {
		return nil, ErrInvalidChannel
	}
	if err := validateChannelConfig(req.Type, req.Config); err != nil {
		return nil, err
	}
	return s.repo.CreateChannel(ctx, req)
}

func (s *Service) UpdateChannel(ctx context.Context, id uint64, req UpdateChannelRequest) (*ChannelDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if req.Config != nil {
		current, err := s.repo.GetChannel(ctx, id)
		if err != nil {
			return nil, err
		}
		if err := validateChannelConfig(current.Type, *req.Config); err != nil {
			return nil, err
		}
	}
	return s.repo.UpdateChannel(ctx, id, req)
}

func (s *Service) DeleteChannel(ctx context.Context, id uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	return s.repo.DeleteChannel(ctx, id)
}

func (s *Service) ListRules(ctx context.Context) ([]RuleDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	return s.repo.ListRules(ctx)
}

func (s *Service) UpdateRule(ctx context.Context, id uint64, req UpdateRuleRequest) (*RuleDTO, error) {
	if s.repo == nil {
		return nil, ErrDependencyUnavailable
	}
	if req.Threshold != nil && *req.Threshold < 1 {
		return nil, ErrInvalidChannel
	}
	return s.repo.UpdateRule(ctx, id, req)
}

func (s *Service) TestChannel(ctx context.Context, id uint64) error {
	if s.repo == nil {
		return ErrDependencyUnavailable
	}
	ch, err := s.repo.GetChannel(ctx, id)
	if err != nil {
		return err
	}
	provider, err := buildProvider(ch.Type, ch.Config)
	if err != nil {
		return err
	}
	return provider.Send(ctx, push.Message{
		Title:   "推送测试",
		Content: "这是一条测试消息，如果你收到了说明推送配置正确。",
	})
}

func validateChannelConfig(channelType string, cfg json.RawMessage) error {
	switch channelType {
	case "bark":
		var c struct {
			DeviceKey string `json:"device_key"`
		}
		if err := json.Unmarshal(cfg, &c); err != nil || strings.TrimSpace(c.DeviceKey) == "" {
			return ErrInvalidChannel
		}
	case "wpush":
		var c struct {
			APIKey string `json:"api_key"`
		}
		if err := json.Unmarshal(cfg, &c); err != nil || strings.TrimSpace(c.APIKey) == "" {
			return ErrInvalidChannel
		}
	default:
		return ErrInvalidChannel
	}
	return nil
}

func buildProvider(channelType string, cfg json.RawMessage) (push.Provider, error) {
	switch channelType {
	case "bark":
		var c struct {
			DeviceKey string `json:"device_key"`
			Server    string `json:"server"`
		}
		if err := json.Unmarshal(cfg, &c); err != nil {
			return nil, err
		}
		return push.NewBarkProvider(push.BarkConfig{DeviceKey: c.DeviceKey, Server: c.Server})
	case "wpush":
		var c struct {
			APIKey string `json:"api_key"`
		}
		if err := json.Unmarshal(cfg, &c); err != nil {
			return nil, err
		}
		return push.NewWPushProvider(push.WPushConfig{APIKey: c.APIKey})
	default:
		return nil, errors.New("unknown channel type")
	}
}
