package notificationimp

import (
	"context"

	"github.com/tech/sendico/notification/interface/api"
	"github.com/tech/sendico/notification/internal/appversion"
	mmail "github.com/tech/sendico/notification/internal/server/notificationimp/mail"
	"github.com/tech/sendico/notification/internal/server/notificationimp/telegram"
	"github.com/tech/sendico/pkg/discovery"
	"github.com/tech/sendico/pkg/domainprovider"
	"github.com/tech/sendico/pkg/merrors"
	msg "github.com/tech/sendico/pkg/messaging"
	na "github.com/tech/sendico/pkg/messaging/notifications/account"
	cnotifications "github.com/tech/sendico/pkg/messaging/notifications/confirmation"
	confirmations "github.com/tech/sendico/pkg/messaging/notifications/confirmations"
	ni "github.com/tech/sendico/pkg/messaging/notifications/invitation"
	snotifications "github.com/tech/sendico/pkg/messaging/notifications/site"
	tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
	"github.com/tech/sendico/pkg/mservice"
	"go.uber.org/zap"
)

type NotificationAPI struct {
	logger    mlogger.Logger
	client    mmail.Client
	dp        domainprovider.DomainProvider
	tg        telegram.Client
	producer  msg.Producer
	announcer *discovery.Announcer
}

func (a *NotificationAPI) Name() mservice.Type {
	return mservice.Notifications
}

func (a *NotificationAPI) Finish(_ context.Context) error {
	if a.announcer != nil {
		a.announcer.Stop()
	}
	return nil
}

func CreateAPI(a api.API) (*NotificationAPI, error) {
	p := &NotificationAPI{
		dp: a.DomainProvider(),
	}
	p.logger = a.Logger().Named(p.Name())
	p.producer = a.Register().Producer()

	if a.Config().Notification == nil {
		return nil, merrors.InvalidArgument("notification configuration is missing", "config.notification")
	}
	if a.Config().Notification.Telegram == nil {
		return nil, merrors.InvalidArgument("telegram configuration is missing", "config.notification.telegram")
	}

	var err error
	if p.client, err = mmail.CreateMailClient(p.logger.Named("mailer"), p.Name(), a.Register().Producer(), a.Localizer(), a.DomainProvider(), a.Config().Notification); err != nil {
		p.logger.Error("Failed to create mail connection", zap.Error(err), zap.String("driver", a.Config().Notification.Driver))
		return nil, err
	}
	if p.tg, err = telegram.NewClient(p.logger.Named("telegram"), a.Config().Notification.Telegram); err != nil {
		p.logger.Error("Failed to create telegram client", zap.Error(err))
		return nil, err
	}

	db, err := a.DBFactory().NewAccountDB()
	if err != nil {
		p.logger.Error("Failed to create account db connection", zap.Error(err))
		return nil, err
	}
	if err := a.Register().Consumer(na.NewAccountCreatedMessageProcessor(p.logger, db, p.onAccount)); err != nil {
		p.logger.Error("Failed to create account creation handler", zap.Error(err))
		return nil, err
	}

	if err := a.Register().Consumer(na.NewPasswordResetRequestedMessageProcessor(p.logger, db, p.onPasswordReset)); err != nil {
		p.logger.Error("Failed to create password reset handler", zap.Error(err))
		return nil, err
	}

	if err := a.Register().Consumer(cnotifications.NewConfirmationCodeProcessor(p.logger, db, p.onConfirmationCode)); err != nil {
		p.logger.Error("Failed to create confirmation code handler", zap.Error(err))
		return nil, err
	}
	if err := a.Register().Consumer(confirmations.NewConfirmationRequestProcessor(p.logger, p.onConfirmationRequest)); err != nil {
		p.logger.Error("Failed to register confirmation request handler", zap.Error(err))
		return nil, err
	}
	if err := a.Register().Consumer(tnotifications.NewTelegramTextProcessor(p.logger, p.onTelegramText)); err != nil {
		p.logger.Error("Failed to register telegram text handler", zap.Error(err))
		return nil, err
	}
	if err := a.Register().Consumer(tnotifications.NewTelegramReactionProcessor(p.logger, p.onTelegramReaction)); err != nil {
		p.logger.Error("Failed to register telegram reaction handler", zap.Error(err))
		return nil, err
	}

	idb, err := a.DBFactory().NewInvitationsDB()
	if err != nil {
		p.logger.Error("Failed to create invitation db connection", zap.Error(err))
		return nil, err
	}
	if err := a.Register().Consumer(ni.NewInvitationCreatedProcessor(p.logger, p.onInvitation, idb, db)); err != nil {
		p.logger.Error("Failed to create invitation creation handler", zap.Error(err))
		return nil, err
	}

	if err := a.Register().Consumer(snotifications.NewSiteRequestProcessor(p.logger, p.onDemoRequest, p.onContactRequest, p.onCallRequest)); err != nil {
		p.logger.Error("Failed to register site request handler", zap.Error(err))
		return nil, err
	}

	if router := a.Router(); router != nil {
		router.Post("/telegram/webhook", p.handleTelegramWebhook)
	}

	announce := discovery.Announcement{
		Service:    mservice.Notifications,
		Operations: []string{discovery.OperationNotifySend},
		Version:    appversion.Create().Short(),
	}
	p.announcer = discovery.NewAnnouncer(p.logger, a.Register().Producer(), mservice.Notifications, announce)
	p.announcer.Start()

	return p, nil
}

func (a *NotificationAPI) onDemoRequest(ctx context.Context, request *model.DemoRequest) error {
	if a.tg == nil {
		return merrors.Internal("telegram client is not configured")
	}
	if err := a.tg.SendDemoRequest(ctx, request); err != nil {
		a.logger.Warn("Failed to send demo request via telegram", zap.Error(err))
		return err
	}
	a.logger.Info("Demo request sent via Telegram", zap.String("name", request.Name), zap.String("organization", request.OrganizationName))
	return nil
}

func (a *NotificationAPI) onContactRequest(ctx context.Context, request *model.ContactRequest) error {
	if a.tg == nil {
		return merrors.Internal("telegram client is not configured")
	}
	if err := a.tg.SendContactRequest(ctx, request); err != nil {
		a.logger.Warn("Failed to send contact request via telegram", zap.Error(err))
		return err
	}
	a.logger.Info("Contact request sent via Telegram", zap.String("name", request.Name), zap.String("topic", request.Topic))
	return nil
}

func (a *NotificationAPI) onCallRequest(ctx context.Context, request *model.CallRequest) error {
	if a.tg == nil {
		return merrors.Internal("telegram client is not configured")
	}
	if err := a.tg.SendCallRequest(ctx, request); err != nil {
		a.logger.Warn("Failed to send call request via telegram", zap.Error(err))
		return err
	}
	a.logger.Info("Call request sent via Telegram", zap.String("phone", request.Phone))
	return nil
}
