package services

import (
	"errors"
	"time"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/db"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/notify"
	"github.com/google/uuid"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
)

func ListChannels(instanceID string) ([]models.NotificationChannel, error) {
	ctx, cancel := monCtx()
	defer cancel()
	cur, err := db.Col("notification_channels").Find(ctx, bson.M{"instance_id": instanceID}, options.Find().SetSort(bson.M{"created_at": 1}))
	if err != nil {
		return nil, err
	}
	var out []models.NotificationChannel
	if err := cur.All(ctx, &out); err != nil {
		return nil, err
	}
	return out, nil
}

func GetChannel(instanceID, channelID string) (*models.NotificationChannel, error) {
	ctx, cancel := monCtx()
	defer cancel()
	var ch models.NotificationChannel
	err := db.Col("notification_channels").FindOne(ctx, bson.M{"channel_id": channelID, "instance_id": instanceID}).Decode(&ch)
	if errors.Is(err, mongo.ErrNoDocuments) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &ch, nil
}

func GetChannels(instanceID string, channelIDs []string) ([]models.NotificationChannel, error) {
	if len(channelIDs) == 0 {
		return nil, nil
	}
	ctx, cancel := monCtx()
	defer cancel()
	cur, err := db.Col("notification_channels").Find(ctx, bson.M{"instance_id": instanceID, "channel_id": bson.M{"$in": channelIDs}})
	if err != nil {
		return nil, err
	}
	var out []models.NotificationChannel
	if err := cur.All(ctx, &out); err != nil {
		return nil, err
	}
	return out, nil
}

func validateChannelIDs(instanceID string, channelIDs []string) error {
	for _, id := range channelIDs {
		ch, err := GetChannel(instanceID, id)
		if err != nil {
			return err
		}
		if ch == nil {
			return errors.New("channel " + id + " not found")
		}
	}
	return nil
}

func CreateChannel(instanceID string, ch *models.NotificationChannel) (*models.NotificationChannel, error) {
	if err := CheckChannelLimit(instanceID); err != nil {
		return nil, err
	}
	ctx, cancel := monCtx()
	defer cancel()
	ch.InstanceID = instanceID
	ch.ChannelID = uuid.NewString()
	ch.CreatedAt = time.Now()
	if ch.Config == nil {
		ch.Config = map[string]string{}
	}
	if _, err := db.Col("notification_channels").InsertOne(ctx, ch); err != nil {
		return nil, err
	}
	return ch, nil
}

func UpdateChannel(instanceID, channelID string, upd bson.M) error {
	ctx, cancel := monCtx()
	defer cancel()
	_, err := db.Col("notification_channels").UpdateOne(ctx, bson.M{"channel_id": channelID, "instance_id": instanceID}, bson.M{"$set": upd})
	return err
}

func DeleteChannel(instanceID, channelID string) error {
	ctx, cancel := monCtx()
	defer cancel()
	_, err := db.Col("notification_channels").DeleteOne(ctx, bson.M{"channel_id": channelID, "instance_id": instanceID})
	return err
}

func TestChannel(instanceID, channelID string) error {
	ch, err := GetChannel(instanceID, channelID)
	if err != nil {
		return err
	}
	if ch == nil {
		return errors.New("channel not found")
	}
	return notify.Test(*ch)
}
