package processor

import (
	"context"
	"fmt"
	"net/url"

	"eslogad-be/internal/config"
	"eslogad-be/internal/contract"
	"eslogad-be/internal/entities"

	"github.com/google/uuid"
	novu "github.com/novuhq/go-novu/lib"
)

type NotificationProcessor interface {
	// User management
	CreateSubscriber(ctx context.Context, user *entities.User) error
	UpdateSubscriber(ctx context.Context, user *entities.User) error
	DeleteSubscriber(ctx context.Context, userID uuid.UUID) error
	CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error
	BulkCreateSubscribers(ctx context.Context, users []*entities.User) error

	// Letter notifications
	SendIncomingLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error
}

type NotificationProcessorImpl struct {
	provider   NotificationProvider
	workflowID string
}

func NewNotificationProcessor(provider NotificationProvider, workflowID string) *NotificationProcessorImpl {
	return &NotificationProcessorImpl{
		provider:   provider,
		workflowID: workflowID,
	}
}

func (p *NotificationProcessorImpl) CreateSubscriber(ctx context.Context, user *entities.User) error {
	return p.provider.CreateSubscriber(ctx, user)
}

func (p *NotificationProcessorImpl) UpdateSubscriber(ctx context.Context, user *entities.User) error {
	return p.provider.UpdateSubscriber(ctx, user)
}

func (p *NotificationProcessorImpl) DeleteSubscriber(ctx context.Context, userID uuid.UUID) error {
	return p.provider.DeleteSubscriber(ctx, userID)
}

func (p *NotificationProcessorImpl) CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error {
	return p.provider.CreateSubscriberFromContract(ctx, user)
}

func (p *NotificationProcessorImpl) BulkCreateSubscribers(ctx context.Context, users []*entities.User) error {
	return p.provider.BulkCreateSubscribers(ctx, users)
}

func (p *NotificationProcessorImpl) SendIncomingLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error {
	// Ensure subscriber exists
	if err := p.provider.EnsureSubscriberExists(ctx, recipientUserID); err != nil {
		return fmt.Errorf("failed to ensure subscriber exists: %w", err)
	}

	// Build notification URL
	url := fmt.Sprintf("/en/apps/surat-menyurat/masuk-detail/%s", letterID.String())

	// Use workflow ID from config (defaults to "notification-dashbpard")
	workflowID := p.workflowID
	if workflowID == "" {
		workflowID = "notification-dashbpard"
	}

	// Send notification
	return p.provider.SendNotification(ctx, NotificationPayload{
		RecipientID: recipientUserID,
		EventName:   workflowID,
		Data: map[string]interface{}{
			"subject": subject,
			"body":    body,
			"url":     url,
		},
	})
}

// NotificationProvider interface for different notification services
type NotificationProvider interface {
	// User management
	CreateSubscriber(ctx context.Context, user *entities.User) error
	UpdateSubscriber(ctx context.Context, user *entities.User) error
	DeleteSubscriber(ctx context.Context, userID uuid.UUID) error
	CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error
	BulkCreateSubscribers(ctx context.Context, users []*entities.User) error

	// Core notification methods
	EnsureSubscriberExists(ctx context.Context, userID uuid.UUID) error
	SendNotification(ctx context.Context, payload NotificationPayload) error
}

type NotificationPayload struct {
	RecipientID uuid.UUID
	EventName   string
	Data        map[string]interface{}
}

// NovuProvider implements NotificationProvider using Novu
type NovuProvider struct {
	client *novu.APIClient
	config *config.NovuConfig
}

func NewNovuProvider(cfg *config.NovuConfig) *NovuProvider {
	if cfg.APIKey == "" {
		return &NovuProvider{
			client: nil,
			config: cfg,
		}
	}

	// Create Novu config with backend URL
	novuConfig := &novu.Config{}
	if cfg.BaseURL != "" {
		backendURL, err := url.Parse(cfg.BaseURL)
		if err == nil {
			novuConfig.BackendURL = backendURL
		}
	}

	client := novu.NewAPIClient(cfg.APIKey, novuConfig)

	return &NovuProvider{
		client: client,
		config: cfg,
	}
}

func (p *NovuProvider) CreateSubscriber(ctx context.Context, user *entities.User) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	subscriberID := user.ID.String()

	data := map[string]interface{}{
		"userId":    user.ID.String(),
		"email":     user.Email,
		"isActive":  user.IsActive,
		"createdAt": user.CreatedAt,
	}

	if user.Departments != nil && len(user.Departments) > 0 {
		depts := make([]map[string]interface{}, len(user.Departments))
		for i, dept := range user.Departments {
			depts[i] = map[string]interface{}{
				"id":   dept.ID.String(),
				"name": dept.Name,
				"code": dept.Code,
			}
		}
		data["departments"] = depts
	}

	subscriber := novu.SubscriberPayload{
		Email:     user.Email,
		FirstName: user.Name,
		LastName:  "",
		Phone:     "",
		Avatar:    "",
		Data:      data,
	}

	_, err := p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
	if err != nil {
		return fmt.Errorf("failed to create subscriber: %w", err)
	}

	return nil
}

func (p *NovuProvider) UpdateSubscriber(ctx context.Context, user *entities.User) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	subscriberID := user.ID.String()

	data := map[string]interface{}{
		"userId":    user.ID.String(),
		"email":     user.Email,
		"isActive":  user.IsActive,
		"updatedAt": user.UpdatedAt,
	}

	if user.Departments != nil && len(user.Departments) > 0 {
		depts := make([]map[string]interface{}, len(user.Departments))
		for i, dept := range user.Departments {
			depts[i] = map[string]interface{}{
				"id":   dept.ID.String(),
				"name": dept.Name,
				"code": dept.Code,
			}
		}
		data["departments"] = depts
	}

	updateData := novu.SubscriberPayload{
		Email:     user.Email,
		FirstName: user.Name,
		LastName:  "",
		Phone:     "",
		Avatar:    "",
		Data:      data,
	}

	_, err := p.client.SubscriberApi.Update(ctx, subscriberID, updateData)
	if err != nil {
		return fmt.Errorf("failed to update subscriber: %w", err)
	}

	return nil
}

func (p *NovuProvider) DeleteSubscriber(ctx context.Context, userID uuid.UUID) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	subscriberID := userID.String()

	_, err := p.client.SubscriberApi.Delete(ctx, subscriberID)
	if err != nil {
		return fmt.Errorf("failed to delete subscriber: %w", err)
	}

	return nil
}

func (p *NovuProvider) CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	subscriberID := user.ID.String()

	data := map[string]interface{}{
		"userId":    user.ID.String(),
		"email":     user.Email,
		"isActive":  user.IsActive,
		"createdAt": user.CreatedAt,
	}

	if user.Roles != nil && len(user.Roles) > 0 {
		roles := make([]map[string]interface{}, len(user.Roles))
		for i, role := range user.Roles {
			roles[i] = map[string]interface{}{
				"id":   role.ID.String(),
				"name": role.Name,
				"code": role.Code,
			}
		}
		data["roles"] = roles
	}

	if user.DepartmentResponse != nil && len(user.DepartmentResponse) > 0 {
		depts := make([]map[string]interface{}, len(user.DepartmentResponse))
		for i, dept := range user.DepartmentResponse {
			depts[i] = map[string]interface{}{
				"id":   dept.ID.String(),
				"name": dept.Name,
				"code": dept.Code,
			}
		}
		data["departments"] = depts
	}

	subscriber := novu.SubscriberPayload{
		Email:     user.Email,
		FirstName: user.Name,
		LastName:  "",
		Phone:     "",
		Avatar:    "",
		Data:      data,
	}

	_, err := p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
	if err != nil {
		return fmt.Errorf("failed to create subscriber from contract: %w", err)
	}

	return nil
}

func (p *NovuProvider) BulkCreateSubscribers(ctx context.Context, users []*entities.User) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	var lastErr error
	successCount := 0

	for _, user := range users {
		err := p.CreateSubscriber(ctx, user)
		if err != nil {
			lastErr = err
			continue
		}
		successCount++
	}

	if lastErr != nil && successCount == 0 {
		return fmt.Errorf("failed to create any subscribers, last error: %w", lastErr)
	}

	if lastErr != nil {
		return fmt.Errorf("created %d out of %d subscribers, last error: %w", successCount, len(users), lastErr)
	}

	return nil
}

func (p *NovuProvider) EnsureSubscriberExists(ctx context.Context, userID uuid.UUID) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	subscriberID := userID.String()

	// Check if subscriber exists
	_, err := p.client.SubscriberApi.Get(ctx, subscriberID)
	if err != nil {
		// Subscriber doesn't exist, create a basic one
		subscriber := novu.SubscriberPayload{
			Email: fmt.Sprintf("%s@placeholder.com", subscriberID),
		}
		_, err = p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
		if err != nil {
			return fmt.Errorf("failed to ensure subscriber exists: %w", err)
		}
	}

	return nil
}

func (p *NovuProvider) SendNotification(ctx context.Context, payload NotificationPayload) error {
	if p.client == nil {
		return fmt.Errorf("novu client not initialized")
	}

	triggerPayload := novu.ITriggerPayloadOptions{
		To:      payload.RecipientID.String(),
		Payload: payload.Data,
	}

	_, err := p.client.EventApi.Trigger(ctx, payload.EventName, triggerPayload)
	if err != nil {
		return fmt.Errorf("failed to send notification: %w", err)
	}

	return nil
}
