package adminpush

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

	"gorm.io/gorm"
)

type Repository struct {
	db *gorm.DB
}

func NewRepository(db *gorm.DB) *Repository {
	return &Repository{db: db}
}

// ── 渠道 CRUD ──

func (r *Repository) ListChannels(ctx context.Context) ([]ChannelDTO, error) {
	var rows []pushChannel
	if err := r.db.WithContext(ctx).Order("id ASC").Find(&rows).Error; err != nil {
		return nil, err
	}
	items := make([]ChannelDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, toChannelDTO(row))
	}
	return items, nil
}

func (r *Repository) CreateChannel(ctx context.Context, req CreateChannelRequest) (*ChannelDTO, error) {
	cfgJSON, err := json.Marshal(req.Config)
	if err != nil {
		return nil, err
	}
	row := pushChannel{
		Name:    req.Name,
		Type:    req.Type,
		Config:  cfgJSON,
		Enabled: true,
	}
	if err := r.db.WithContext(ctx).Create(&row).Error; err != nil {
		return nil, err
	}
	dto := toChannelDTO(row)
	return &dto, nil
}

func (r *Repository) UpdateChannel(ctx context.Context, id uint64, req UpdateChannelRequest) (*ChannelDTO, error) {
	var row pushChannel
	if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrChannelNotFound
		}
		return nil, err
	}
	if req.Name != nil {
		row.Name = *req.Name
	}
	if req.Config != nil {
		cfgJSON, err := json.Marshal(req.Config)
		if err != nil {
			return nil, err
		}
		row.Config = cfgJSON
	}
	if req.Enabled != nil {
		row.Enabled = *req.Enabled
	}
	if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
		return nil, err
	}
	dto := toChannelDTO(row)
	return &dto, nil
}

func (r *Repository) DeleteChannel(ctx context.Context, id uint64) error {
	result := r.db.WithContext(ctx).Delete(&pushChannel{}, id)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return ErrChannelNotFound
	}
	return nil
}

// ── 规则 CRUD ──

func (r *Repository) ListRules(ctx context.Context) ([]RuleDTO, error) {
	var rows []pushRule
	if err := r.db.WithContext(ctx).Order("id ASC").Find(&rows).Error; err != nil {
		return nil, err
	}
	items := make([]RuleDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, toRuleDTO(row))
	}
	return items, nil
}

func (r *Repository) UpdateRule(ctx context.Context, id uint64, req UpdateRuleRequest) (*RuleDTO, error) {
	var row pushRule
	if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrRuleNotFound
		}
		return nil, err
	}
	if req.Enabled != nil {
		row.Enabled = *req.Enabled
	}
	if req.Threshold != nil {
		row.Threshold = *req.Threshold
	}
	if req.MessageTemplate != nil {
		row.MessageTemplate = *req.MessageTemplate
	}
	if err := r.db.WithContext(ctx).Save(&row).Error; err != nil {
		return nil, err
	}
	dto := toRuleDTO(row)
	return &dto, nil
}

// ── 推送调用方使用 ──

func (r *Repository) GetChannel(ctx context.Context, id uint64) (*ChannelDTO, error) {
	var row pushChannel
	if err := r.db.WithContext(ctx).First(&row, id).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, ErrChannelNotFound
		}
		return nil, err
	}
	dto := toChannelDTO(row)
	return &dto, nil
}

func (r *Repository) GetActiveChannels(ctx context.Context) ([]ChannelDTO, error) {
	var rows []pushChannel
	if err := r.db.WithContext(ctx).Where("enabled = ?", true).Find(&rows).Error; err != nil {
		return nil, err
	}
	items := make([]ChannelDTO, 0, len(rows))
	for _, row := range rows {
		items = append(items, toChannelDTO(row))
	}
	return items, nil
}

func (r *Repository) GetRule(ctx context.Context, event string) (*RuleDTO, error) {
	var row pushRule
	if err := r.db.WithContext(ctx).Where("event = ?", event).First(&row).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, nil
		}
		return nil, err
	}
	dto := toRuleDTO(row)
	return &dto, nil
}
