package store

import (
	"context"
	"errors"
	"strings"

	"github.com/tech/sendico/gateway/tgsettle/storage"
	"github.com/tech/sendico/gateway/tgsettle/storage/model"
	"github.com/tech/sendico/pkg/db/repository"
	ri "github.com/tech/sendico/pkg/db/repository/index"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.uber.org/zap"
)

const (
	treasuryTelegramUsersCollection = "treasury_telegram_users"
	fieldTreasuryTelegramUserID     = "telegramUserId"
)

type TreasuryTelegramUsers struct {
	logger mlogger.Logger
	repo   repository.Repository
}

func NewTreasuryTelegramUsers(logger mlogger.Logger, db *mongo.Database) (*TreasuryTelegramUsers, error) {
	if db == nil {
		return nil, merrors.InvalidArgument("mongo database is nil")
	}
	if logger == nil {
		logger = zap.NewNop()
	}
	logger = logger.Named("treasury_telegram_users").With(zap.String("collection", treasuryTelegramUsersCollection))

	repo := repository.CreateMongoRepository(db, treasuryTelegramUsersCollection)
	if err := repo.CreateIndex(&ri.Definition{
		Keys:   []ri.Key{{Field: fieldTreasuryTelegramUserID, Sort: ri.Asc}},
		Unique: true,
	}); err != nil {
		logger.Error("Failed to create treasury telegram users user_id index", zap.Error(err), zap.String("index_field", fieldTreasuryTelegramUserID))
		return nil, err
	}

	return &TreasuryTelegramUsers{
		logger: logger,
		repo:   repo,
	}, nil
}

func (t *TreasuryTelegramUsers) FindByTelegramUserID(ctx context.Context, telegramUserID string) (*model.TreasuryTelegramUser, error) {
	telegramUserID = strings.TrimSpace(telegramUserID)
	if telegramUserID == "" {
		return nil, merrors.InvalidArgument("telegram_user_id is required", "telegram_user_id")
	}
	var result model.TreasuryTelegramUser
	err := t.repo.FindOneByFilter(ctx, repository.Filter(fieldTreasuryTelegramUserID, telegramUserID), &result)
	if errors.Is(err, merrors.ErrNoData) {
		//nolint:nilnil // Not-found is represented as (nil, nil) by this store contract.
		return nil, nil
	}
	if err != nil {
		if !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {
			t.logger.Warn("Failed to load treasury telegram user", zap.Error(err), zap.String("telegram_user_id", telegramUserID))
		}
		return nil, err
	}
	result.TelegramUserID = strings.TrimSpace(result.TelegramUserID)
	result.LedgerAccountID = strings.TrimSpace(result.LedgerAccountID)
	if len(result.AllowedChatIDs) > 0 {
		normalized := make([]string, 0, len(result.AllowedChatIDs))
		for _, next := range result.AllowedChatIDs {
			next = strings.TrimSpace(next)
			if next == "" {
				continue
			}
			normalized = append(normalized, next)
		}
		result.AllowedChatIDs = normalized
	}
	if result.TelegramUserID == "" || result.LedgerAccountID == "" {
		//nolint:nilnil // Invalid/incomplete records are treated as missing binding.
		return nil, nil
	}
	return &result, nil
}

var _ storage.TreasuryTelegramUsersStore = (*TreasuryTelegramUsers)(nil)
