package push

import (
	"context"
	"fmt"

	"github.com/rs/zerolog/log"
	"github.com/sideshow/apns2"
	"github.com/sideshow/apns2/payload"
	"github.com/sideshow/apns2/token"

	"github.com/treytartt/honeydue-api/internal/config"
)

// APNsClient handles direct communication with Apple Push Notification service
type APNsClient struct {
	client *apns2.Client
	topic  string
}

// NewAPNsClient creates a new APNs client using token-based authentication
func NewAPNsClient(cfg *config.PushConfig) (*APNsClient, error) {
	if cfg.APNSKeyPath == "" || cfg.APNSKeyID == "" || cfg.APNSTeamID == "" {
		return nil, fmt.Errorf("APNs configuration incomplete: key_path=%s, key_id=%s, team_id=%s",
			cfg.APNSKeyPath, cfg.APNSKeyID, cfg.APNSTeamID)
	}

	// Load the APNs auth key (.p8 file)
	authKey, err := token.AuthKeyFromFile(cfg.APNSKeyPath)
	if err != nil {
		return nil, fmt.Errorf("failed to load APNs auth key from %s: %w", cfg.APNSKeyPath, err)
	}

	// Create token for authentication
	authToken := &token.Token{
		AuthKey: authKey,
		KeyID:   cfg.APNSKeyID,
		TeamID:  cfg.APNSTeamID,
	}

	// Create client - sandbox if APNSSandbox is true, production otherwise.
	// APNSSandbox is the single source of truth (defaults to true for safety).
	var client *apns2.Client
	if cfg.APNSSandbox {
		client = apns2.NewTokenClient(authToken).Development()
		log.Info().Msg("APNs client configured for DEVELOPMENT/SANDBOX")
	} else {
		client = apns2.NewTokenClient(authToken).Production()
		log.Info().Msg("APNs client configured for PRODUCTION")
	}

	return &APNsClient{
		client: client,
		topic:  cfg.APNSTopic,
	}, nil
}

// Send sends a push notification to iOS devices
func (c *APNsClient) Send(ctx context.Context, tokens []string, title, message string, data map[string]string) error {
	if len(tokens) == 0 {
		return nil
	}

	// Build the notification payload
	p := payload.NewPayload().
		AlertTitle(title).
		AlertBody(message).
		Sound("default").
		MutableContent()

	// Add custom data
	for key, value := range data {
		p.Custom(key, value)
	}

	var errors []error
	successCount := 0

	for _, deviceToken := range tokens {
		notification := &apns2.Notification{
			DeviceToken: deviceToken,
			Topic:       c.topic,
			Payload:     p,
			Priority:    apns2.PriorityHigh,
		}

		res, err := c.client.PushWithContext(ctx, notification)
		if err != nil {
			log.Error().
				Err(err).
				Str("token", truncateToken(deviceToken)).
				Msg("Failed to send APNs notification")
			errors = append(errors, fmt.Errorf("token %s: %w", truncateToken(deviceToken), err))
			continue
		}

		if !res.Sent() {
			log.Error().
				Str("token", truncateToken(deviceToken)).
				Str("reason", res.Reason).
				Int("status", res.StatusCode).
				Msg("APNs notification not sent")
			errors = append(errors, fmt.Errorf("token %s: %s (status %d)", truncateToken(deviceToken), res.Reason, res.StatusCode))
			continue
		}

		successCount++
		log.Debug().
			Str("token", truncateToken(deviceToken)).
			Str("apns_id", res.ApnsID).
			Msg("APNs notification sent successfully")
	}

	log.Info().
		Int("total", len(tokens)).
		Int("success", successCount).
		Int("failed", len(errors)).
		Msg("APNs batch send complete")

	if len(errors) > 0 && successCount == 0 {
		return fmt.Errorf("all APNs notifications failed: %v", errors)
	}

	return nil
}

// SendWithCategory sends a push notification with iOS category for actionable notifications
func (c *APNsClient) SendWithCategory(ctx context.Context, tokens []string, title, message string, data map[string]string, categoryID string) error {
	if len(tokens) == 0 {
		return nil
	}

	// Build the notification payload with category
	p := payload.NewPayload().
		AlertTitle(title).
		AlertBody(message).
		Sound("default").
		MutableContent().
		Category(categoryID) // iOS category for actionable notifications

	// Add custom data
	for key, value := range data {
		p.Custom(key, value)
	}

	var errors []error
	successCount := 0

	for _, deviceToken := range tokens {
		notification := &apns2.Notification{
			DeviceToken: deviceToken,
			Topic:       c.topic,
			Payload:     p,
			Priority:    apns2.PriorityHigh,
		}

		res, err := c.client.PushWithContext(ctx, notification)
		if err != nil {
			log.Error().
				Err(err).
				Str("token", truncateToken(deviceToken)).
				Str("category", categoryID).
				Msg("Failed to send APNs actionable notification")
			errors = append(errors, fmt.Errorf("token %s: %w", truncateToken(deviceToken), err))
			continue
		}

		if !res.Sent() {
			log.Error().
				Str("token", truncateToken(deviceToken)).
				Str("reason", res.Reason).
				Int("status", res.StatusCode).
				Str("category", categoryID).
				Msg("APNs actionable notification not sent")
			errors = append(errors, fmt.Errorf("token %s: %s (status %d)", truncateToken(deviceToken), res.Reason, res.StatusCode))
			continue
		}

		successCount++
		log.Debug().
			Str("token", truncateToken(deviceToken)).
			Str("apns_id", res.ApnsID).
			Str("category", categoryID).
			Msg("APNs actionable notification sent successfully")
	}

	log.Info().
		Int("total", len(tokens)).
		Int("success", successCount).
		Int("failed", len(errors)).
		Str("category", categoryID).
		Msg("APNs actionable batch send complete")

	if len(errors) > 0 && successCount == 0 {
		return fmt.Errorf("all APNs actionable notifications failed: %v", errors)
	}

	return nil
}

// truncateToken returns first 8 chars of token for logging
func truncateToken(token string) string {
	if len(token) > 8 {
		return token[:8] + "..."
	}
	return token
}
