package repositories

import (
	"context"
	"fmt"
	"primesys_email/src/db"
	"primesys_email/src/logger"
	. "primesys_email/src/models/division"
	"primesys_email/src/utils"

	"go.mongodb.org/mongo-driver/v2/bson"
)

const divisionCollection = "division_login"

func GetDivisionLoginByID(ctx context.Context, divisionID string) (*DivisionLoginModel, error) {

	objectId, err := utils.StringToObjectID(divisionID)
	if err != nil {
		return nil, err
	}

	filter := bson.M{
		"_id":               objectId,
		"role_id":           7,
		"active_status":     true,
		"report_email_sent": true,
	}

	var divisionLogin DivisionLoginModel

	err = db.Database().Collection(divisionCollection).FindOne(ctx, filter).Decode(&divisionLogin)
	if err != nil {
		return nil, err
	}

	return &divisionLogin, nil
}

func GetSubDivisionLoginByID(ctx context.Context, divisionID string) (*DivisionLoginModel, error) {

	objectId, err := utils.StringToObjectID(divisionID)
	if err != nil {
		return nil, err
	}

	filter := bson.M{
		"_id":               objectId,
		"active_status":     true,
		"report_email_sent": true,
	}

	var divisionLogin DivisionLoginModel

	err = db.Database().Collection(divisionCollection).FindOne(ctx, filter).Decode(&divisionLogin)
	if err != nil {
		return nil, err
	}

	return &divisionLogin, nil
}

func GetAllDivisionsByTrackDivisionID(ctx context.Context, trackDivisionID string) ([]DivisionLoginModel, error) {

	track_id := bson.A{trackDivisionID}
	if old, err := bson.ObjectIDFromHex(trackDivisionID); err == nil {
		track_id = append(track_id, old)
	}

	fitler := bson.M{
		"track_division_id": bson.M{"$in": track_id},
		"report_email_sent": true,
		"active_status":     true,
		"device_list": bson.M{
			"$regex": `,\d+,`,
		},
	}

	cursor, err := db.Database().Collection(divisionCollection).Find(ctx, fitler)
	if err != nil {
		return nil, err
	}

	defer cursor.Close(ctx)

	var divisions []DivisionLoginModel

	if err := cursor.All(ctx, &divisions); err != nil {
		return nil, err
	}

	return divisions, nil
}

func GetAllParentDivisions(ctx context.Context) ([]DivisionLoginModel, error) {

	logger.Info("get all parents")

	filter := bson.M{
		"role_id":           7,
		"active_status":     true,
		"report_email_sent": true,
	}

	cursor, err := db.Database().Collection(divisionCollection).Find(ctx, filter)
	if err != nil {
		return nil, err
	}

	defer cursor.Close(ctx)

	var divisions []DivisionLoginModel

	if err := cursor.All(ctx, &divisions); err != nil {
		return nil, err
	}

	return divisions, nil
}

func UpdateReportAndActiveStatus(ctx context.Context, divisionID string) error {
	logger.Info("update func")

	objectId, err := utils.StringToObjectID(divisionID)
	if err != nil {
		return err
	}

	filter := bson.M{
		"_id":               objectId,
		"role_id":           7,
		"active_status":     true,
		"report_email_sent": true,
	}

	update := bson.M{
		"$set": bson.M{
			"report_email_sent": false,
			"active_status":     false,
		},
	}

	result, err := db.Database().Collection(divisionCollection).UpdateOne(ctx, filter, update)

	if err != nil {
		return err
	}

	if result.MatchedCount == 0 {
		return fmt.Errorf("division not found or does not match the filter")
	}

	logger.Info("division updated: %s", objectId.Hex())
	return nil
}

// GetActiveDivisionLoginsByIDs returns the active divisions of the given ids, used for the ids of a division path
func GetActiveDivisionLoginsByIDs(ctx context.Context, divisionIDs []string) ([]DivisionLoginModel, error) {

	objectIds := make([]bson.ObjectID, 0, len(divisionIDs))
	for _, id := range divisionIDs {
		objectId, err := utils.StringToObjectID(id)
		if err != nil {
			return nil, err
		}
		objectIds = append(objectIds, objectId)
	}

	filter := bson.M{
		"_id":           bson.M{"$in": objectIds},
		"active_status": true,
	}

	cursor, err := db.Database().Collection(divisionCollection).Find(ctx, filter)
	if err != nil {
		return nil, err
	}

	defer cursor.Close(ctx)

	var divisions []DivisionLoginModel

	if err := cursor.All(ctx, &divisions); err != nil {
		return nil, err
	}

	return divisions, nil
}

// GetActiveUsernamesByIDs returns the usernames of the active divisions of the given ids,
// in the same order as the ids (path order)
func GetActiveUsernamesByIDs(ctx context.Context, divisionIDs []string) ([]string, error) {

	objectIds := make([]bson.ObjectID, 0, len(divisionIDs))
	for _, id := range divisionIDs {
		objectId, err := utils.StringToObjectID(id)
		if err != nil {
			return nil, err
		}
		objectIds = append(objectIds, objectId)
	}

	pipeline := bson.A{
		bson.M{"$match": bson.M{
			"_id":           bson.M{"$in": objectIds},
			"active_status": true,
		}},
		// keep the order of the given ids
		bson.M{"$addFields": bson.M{
			"order": bson.M{"$indexOfArray": bson.A{objectIds, "$_id"}},
		}},
		bson.M{"$sort": bson.M{"order": 1}},
		bson.M{"$group": bson.M{
			"_id":       nil,
			"usernames": bson.M{"$push": "$user_name"},
		}},
	}

	cursor, err := db.Database().Collection(divisionCollection).Aggregate(ctx, pipeline)
	if err != nil {
		return nil, err
	}

	defer cursor.Close(ctx)

	var result []struct {
		Usernames []string `bson:"usernames"`
	}

	if err := cursor.All(ctx, &result); err != nil {
		return nil, err
	}

	if len(result) == 0 {
		return []string{}, nil
	}

	return result[0].Usernames, nil
}
