package whatsmeow

import (
	"context"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"os"
	"path/filepath"
	"sync"
	"time"

	"git.warky.dev/wdevs/whatshooked/pkg/config"
	"git.warky.dev/wdevs/whatshooked/pkg/events"
	"git.warky.dev/wdevs/whatshooked/pkg/logging"

	qrterminal "github.com/mdp/qrterminal/v3"
	"go.mau.fi/whatsmeow"
	"go.mau.fi/whatsmeow/proto/waCompanionReg"
	"go.mau.fi/whatsmeow/proto/waE2E"
	"go.mau.fi/whatsmeow/store"
	"go.mau.fi/whatsmeow/store/sqlstore"
	"go.mau.fi/whatsmeow/types"
	waEvents "go.mau.fi/whatsmeow/types/events"
	waLog "go.mau.fi/whatsmeow/util/log"
	"google.golang.org/protobuf/proto"
	"rsc.io/qr"

	_ "github.com/mattn/go-sqlite3"
)

// Client represents a WhatsApp connection using whatsmeow
type Client struct {
	id              string
	phoneNumber     string
	sessionPath     string
	client          *whatsmeow.Client
	container       *sqlstore.Container
	eventBus        *events.EventBus
	mediaConfig     config.MediaConfig
	showQR          bool
	keepAliveCancel context.CancelFunc
	qrCode          string
	qrCodePNG       []byte // Cached PNG data
	qrCodeMutex     sync.RWMutex
	onPhoneUpdate   func(accountID, phoneNumber string) // Callback when phone number is updated
}

// NewClient creates a new whatsmeow client
func NewClient(cfg config.WhatsAppConfig, eventBus *events.EventBus, mediaConfig config.MediaConfig, onPhoneUpdate func(accountID, phoneNumber string)) (*Client, error) {
	if cfg.Type != "whatsmeow" && cfg.Type != "" {
		return nil, fmt.Errorf("invalid client type for whatsmeow: %s", cfg.Type)
	}

	sessionPath := cfg.SessionPath
	if sessionPath == "" {
		sessionPath = fmt.Sprintf("./sessions/%s", cfg.ID)
	}

	return &Client{
		id:            cfg.ID,
		phoneNumber:   cfg.PhoneNumber,
		sessionPath:   sessionPath,
		eventBus:      eventBus,
		mediaConfig:   mediaConfig,
		showQR:        cfg.ShowQR,
		onPhoneUpdate: onPhoneUpdate,
	}, nil
}

// Connect establishes a connection to WhatsApp
func (c *Client) Connect(ctx context.Context) error {
	// Ensure session directory exists
	if err := os.MkdirAll(c.sessionPath, 0700); err != nil {
		return fmt.Errorf("failed to create session directory: %w", err)
	}
	// store.SetOSInfo("Linux", store.GetWAVersion())
	store.DeviceProps.PlatformType = waCompanionReg.DeviceProps_CLOUD_API.Enum()
	store.DeviceProps.Os = proto.String("whatshooked.warky.dev")
	store.DeviceProps.RequireFullSync = proto.Bool(false)

	// Create database container for session storage
	dbPath := filepath.Join(c.sessionPath, "session.db")
	dbLog := waLog.Stdout("Database", "ERROR", true)
	container, err := sqlstore.New(ctx, "sqlite3", "file:"+dbPath+"?_foreign_keys=on", dbLog)
	if err != nil {
		return fmt.Errorf("failed to create database container: %w", err)
	}
	c.container = container

	// Get device store
	deviceStore, err := container.GetFirstDevice(ctx)
	if err != nil {
		return fmt.Errorf("failed to get device: %w", err)
	}

	// Set custom client information that will be shown in WhatsApp
	deviceStore.Platform = "git.warky.dev/wdevs/whatshooked"

	// Set PushName BEFORE pairing - this is what shows up in WhatsApp linked devices
	if deviceStore.PushName == "" {
		deviceStore.PushName = fmt.Sprintf("WhatsHooked %s", c.phoneNumber)
	}

	// Save device store to persist device info before pairing
	if err := deviceStore.Save(ctx); err != nil {
		logging.Warn("Failed to save device store", "account_id", c.id, "error", err)
	}

	// Create client
	clientLog := waLog.Stdout("Client", "ERROR", true)
	client := whatsmeow.NewClient(deviceStore, clientLog)
	c.client = client

	// Register event handler
	client.AddEventHandler(func(evt interface{}) {
		c.handleEvent(evt)
	})

	// Connect
	if client.Store.ID == nil {
		// New device, need to pair
		qrChan, _ := client.GetQRChannel(ctx)
		if err := client.Connect(); err != nil {
			c.eventBus.Publish(events.WhatsAppPairFailedEvent(ctx, c.id, err))
			return fmt.Errorf("failed to connect: %w", err)
		}

		// Wait for QR code
		for evt := range qrChan {
			switch evt.Event {
			case "code":
				logging.Info("QR code received for pairing", "account_id", c.id)

				// Generate PNG (this regenerates on each new QR code)
				pngData, err := c.generateQRCodePNG(evt.Code)
				if err != nil {
					logging.Error("Failed to generate QR code PNG", "account_id", c.id, "error", err)
				}

				// Store QR code and PNG (updates cached version)
				c.qrCodeMutex.Lock()
				c.qrCode = evt.Code
				c.qrCodePNG = pngData
				qrCodeSize := len(pngData)
				c.qrCodeMutex.Unlock()

				logging.Debug("QR code PNG updated", "account_id", c.id, "size_bytes", qrCodeSize)

				// Generate QR code URL
				qrURL := c.generateQRCodeURL()

				// Display QR code in terminal
				fmt.Println("\n========================================")
				fmt.Printf("WhatsApp QR Code for account: %s\n", c.id)
				fmt.Printf("Phone: %s\n", c.phoneNumber)
				fmt.Println("========================================")
				fmt.Println("Scan this QR code with WhatsApp on your phone:")
				fmt.Println()
				qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout)
				fmt.Println()
				fmt.Printf("Or open in browser: %s\n", qrURL)
				fmt.Println("========================================")

				// Publish QR code event with URL
				c.eventBus.Publish(events.WhatsAppQRCodeEvent(ctx, c.id, evt.Code, qrURL))

			case "success":
				logging.Info("Pairing successful", "account_id", c.id, "phone", c.phoneNumber)

				// Clear cached QR code after successful pairing
				c.qrCodeMutex.Lock()
				c.qrCode = ""
				c.qrCodePNG = nil
				c.qrCodeMutex.Unlock()

				c.eventBus.Publish(events.WhatsAppPairSuccessEvent(ctx, c.id))

			case "timeout":
				logging.Warn("QR code timeout", "account_id", c.id)

				// Clear cached QR code on timeout
				c.qrCodeMutex.Lock()
				c.qrCode = ""
				c.qrCodePNG = nil
				c.qrCodeMutex.Unlock()

				c.eventBus.Publish(events.WhatsAppQRTimeoutEvent(ctx, c.id))

			case "error":
				logging.Error("QR code error", "account_id", c.id, "error", evt.Error)
				c.eventBus.Publish(events.WhatsAppQRErrorEvent(ctx, c.id, fmt.Errorf("%v", evt.Error)))

			default:
				logging.Info("Pairing event", "account_id", c.id, "event", evt.Event)
				c.eventBus.Publish(events.WhatsAppPairEventGeneric(ctx, c.id, evt.Event, map[string]any{
					"code": evt.Code,
				}))
			}
		}
	} else {
		// Already paired, just connect
		if err := client.Connect(); err != nil {
			return fmt.Errorf("failed to connect: %w", err)
		}
	}

	// PushName is already set before pairing, no need to set it again here

	if client.IsConnected() {
		err := client.SendPresence(ctx, types.PresenceAvailable)
		if err != nil {
			logging.Warn("Failed to send presence", "account_id", c.id, "error", err)
		} else {
			logging.Debug("Sent presence update", "account_id", c.id)
		}
	}

	// Start keep-alive routine
	c.startKeepAlive()

	logging.Info("WhatsApp client connected", "account_id", c.id, "phone", c.phoneNumber)
	return nil
}

// Disconnect closes the WhatsApp connection
func (c *Client) Disconnect() error {
	// Stop keep-alive
	if c.keepAliveCancel != nil {
		c.keepAliveCancel()
	}

	if c.client != nil {
		c.client.Disconnect()
	}

	logging.Info("WhatsApp client disconnected", "account_id", c.id)
	return nil
}

// IsConnected returns whether the client is connected
func (c *Client) IsConnected() bool {
	if c.client == nil {
		return false
	}
	return c.client.IsConnected()
}

// GetID returns the client ID
func (c *Client) GetID() string {
	return c.id
}

// GetPhoneNumber returns the phone number
func (c *Client) GetPhoneNumber() string {
	return c.phoneNumber
}

// GetType returns the client type
func (c *Client) GetType() string {
	return "whatsmeow"
}

// SendTextMessage sends a text message
func (c *Client) SendTextMessage(ctx context.Context, jid types.JID, text string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	if c.client == nil {
		err := fmt.Errorf("client not initialized")
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), text, err))
		return "", err
	}

	msg := &waE2E.Message{
		Conversation: proto.String(text),
	}

	resp, err := c.client.SendMessage(ctx, jid, msg)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), text, err))
		return "", fmt.Errorf("failed to send message: %w", err)
	}

	logging.Debug("Message sent", "account_id", c.id, "to", jid.String())
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, resp.ID, jid.String(), text))
	return resp.ID, nil
}

// SendImage sends an image message
func (c *Client) SendImage(ctx context.Context, jid types.JID, imageData []byte, mimeType string, caption string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	if c.client == nil {
		err := fmt.Errorf("client not initialized")
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", err
	}

	// Upload the image
	uploaded, err := c.client.Upload(ctx, imageData, whatsmeow.MediaImage)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to upload image: %w", err)
	}

	// Create image message
	msg := &waE2E.Message{
		ImageMessage: &waE2E.ImageMessage{
			URL:           proto.String(uploaded.URL),
			DirectPath:    proto.String(uploaded.DirectPath),
			MediaKey:      uploaded.MediaKey,
			Mimetype:      proto.String(mimeType),
			FileEncSHA256: uploaded.FileEncSHA256,
			FileSHA256:    uploaded.FileSHA256,
			FileLength:    proto.Uint64(uint64(len(imageData))),
		},
	}

	// Add caption if provided
	if caption != "" {
		msg.ImageMessage.Caption = proto.String(caption)
	}

	// Send the message
	resp, err := c.client.SendMessage(ctx, jid, msg)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to send image: %w", err)
	}

	logging.Debug("Image sent", "account_id", c.id, "to", jid.String())
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, resp.ID, jid.String(), caption))
	return resp.ID, nil
}

// SendVideo sends a video message
func (c *Client) SendVideo(ctx context.Context, jid types.JID, videoData []byte, mimeType string, caption string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	if c.client == nil {
		err := fmt.Errorf("client not initialized")
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", err
	}

	// Upload the video
	uploaded, err := c.client.Upload(ctx, videoData, whatsmeow.MediaVideo)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to upload video: %w", err)
	}

	// Create video message
	msg := &waE2E.Message{
		VideoMessage: &waE2E.VideoMessage{
			URL:           proto.String(uploaded.URL),
			DirectPath:    proto.String(uploaded.DirectPath),
			MediaKey:      uploaded.MediaKey,
			Mimetype:      proto.String(mimeType),
			FileEncSHA256: uploaded.FileEncSHA256,
			FileSHA256:    uploaded.FileSHA256,
			FileLength:    proto.Uint64(uint64(len(videoData))),
		},
	}

	// Add caption if provided
	if caption != "" {
		msg.VideoMessage.Caption = proto.String(caption)
	}

	// Send the message
	resp, err := c.client.SendMessage(ctx, jid, msg)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to send video: %w", err)
	}

	logging.Debug("Video sent", "account_id", c.id, "to", jid.String())
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, resp.ID, jid.String(), caption))
	return resp.ID, nil
}

// SendDocument sends a document message
func (c *Client) SendDocument(ctx context.Context, jid types.JID, documentData []byte, mimeType string, filename string, caption string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}

	if c.client == nil {
		err := fmt.Errorf("client not initialized")
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", err
	}

	// Upload the document
	uploaded, err := c.client.Upload(ctx, documentData, whatsmeow.MediaDocument)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to upload document: %w", err)
	}

	// Create document message
	msg := &waE2E.Message{
		DocumentMessage: &waE2E.DocumentMessage{
			URL:           proto.String(uploaded.URL),
			DirectPath:    proto.String(uploaded.DirectPath),
			MediaKey:      uploaded.MediaKey,
			Mimetype:      proto.String(mimeType),
			FileEncSHA256: uploaded.FileEncSHA256,
			FileSHA256:    uploaded.FileSHA256,
			FileLength:    proto.Uint64(uint64(len(documentData))),
			FileName:      proto.String(filename),
		},
	}

	// Add caption if provided
	if caption != "" {
		msg.DocumentMessage.Caption = proto.String(caption)
	}

	// Send the message
	resp, err := c.client.SendMessage(ctx, jid, msg)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, jid.String(), caption, err))
		return "", fmt.Errorf("failed to send document: %w", err)
	}

	logging.Debug("Document sent", "account_id", c.id, "to", jid.String(), "filename", filename)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, resp.ID, jid.String(), caption))
	return resp.ID, nil
}

// handleEvent processes WhatsApp events
func (c *Client) handleEvent(evt interface{}) {
	ctx := context.Background()

	switch v := evt.(type) {
	case *waEvents.Message:
		logging.Debug("Message received", "account_id", c.id, "from", v.Info.Sender.String())

		// Extract message content based on type
		var text string
		var messageType = "text"
		var mimeType string
		var filename string
		var mediaBase64 string
		var mediaURL string

		// Handle text messages
		if v.Message.Conversation != nil {
			text = *v.Message.Conversation
			messageType = "text"
		} else if v.Message.ExtendedTextMessage != nil && v.Message.ExtendedTextMessage.Text != nil {
			text = *v.Message.ExtendedTextMessage.Text
			messageType = "text"
		}

		// Handle image messages
		if v.Message.ImageMessage != nil {
			img := v.Message.ImageMessage
			messageType = "image"
			mimeType = img.GetMimetype()

			if img.Caption != nil {
				text = *img.Caption
			}

			// Download image
			data, err := c.client.Download(ctx, img)
			if err != nil {
				logging.Error("Failed to download image", "account_id", c.id, "error", err)
			} else {
				filename, mediaURL = c.processMediaData(v.Info.ID, data, mimeType, &mediaBase64)
			}
		}

		// Handle video messages
		if v.Message.VideoMessage != nil {
			vid := v.Message.VideoMessage
			messageType = "video"
			mimeType = vid.GetMimetype()

			if vid.Caption != nil {
				text = *vid.Caption
			}

			// Download video
			data, err := c.client.Download(ctx, vid)
			if err != nil {
				logging.Error("Failed to download video", "account_id", c.id, "error", err)
			} else {
				filename, mediaURL = c.processMediaData(v.Info.ID, data, mimeType, &mediaBase64)
			}
		}

		// Handle document messages
		if v.Message.DocumentMessage != nil {
			doc := v.Message.DocumentMessage
			messageType = "document"
			mimeType = doc.GetMimetype()

			if doc.FileName != nil {
				filename = *doc.FileName
			}

			if doc.Caption != nil {
				text = *doc.Caption
			}

			// Download document
			data, err := c.client.Download(ctx, doc)
			if err != nil {
				logging.Error("Failed to download document", "account_id", c.id, "error", err)
			} else {
				filename, mediaURL = c.processMediaData(v.Info.ID, data, mimeType, &mediaBase64)
			}
		}

		// Publish message received event
		c.eventBus.Publish(events.MessageReceivedEvent(
			ctx,
			c.id,
			v.Info.ID,
			v.Info.Sender.String(),
			v.Info.Chat.String(),
			text,
			v.Info.Timestamp,
			v.Info.IsGroup,
			"", // group name - TODO: extract from message
			"", // sender name - TODO: extract from message
			messageType,
			mimeType,
			filename,
			mediaBase64,
			mediaURL,
		))

	case *waEvents.Connected:
		logging.Info("WhatsApp connected", "account_id", c.id)

		// Get the actual phone number from WhatsApp
		phoneNumber := ""
		if c.client.Store.ID != nil {
			actualPhone := c.client.Store.ID.User
			phoneNumber = "+" + actualPhone

			// Update phone number in client if it's different
			if c.phoneNumber != phoneNumber {
				c.phoneNumber = phoneNumber
				logging.Info("Updated phone number from WhatsApp", "account_id", c.id, "phone", phoneNumber)

				// Trigger config update callback to persist the phone number
				if c.onPhoneUpdate != nil {
					c.onPhoneUpdate(c.id, phoneNumber)
				}
			}
		} else if c.phoneNumber != "" {
			phoneNumber = c.phoneNumber
		}

		c.eventBus.Publish(events.WhatsAppConnectedEvent(ctx, c.id, phoneNumber))

	case *waEvents.Disconnected:
		logging.Warn("WhatsApp disconnected", "account_id", c.id)
		c.eventBus.Publish(events.WhatsAppDisconnectedEvent(ctx, c.id, "connection lost"))

	case *waEvents.Receipt:
		// Handle delivery and read receipts
		switch v.Type {
		case types.ReceiptTypeDelivered:
			for _, messageID := range v.MessageIDs {
				logging.Debug("Message delivered", "account_id", c.id, "message_id", messageID, "from", v.Sender.String())
				c.eventBus.Publish(events.MessageDeliveredEvent(ctx, c.id, messageID, v.Sender.String(), v.Timestamp))
			}
		case types.ReceiptTypeRead:
			for _, messageID := range v.MessageIDs {
				logging.Debug("Message read", "account_id", c.id, "message_id", messageID, "from", v.Sender.String())
				c.eventBus.Publish(events.MessageReadEvent(ctx, c.id, messageID, v.Sender.String(), v.Timestamp))
			}
		}
	}
}

// startKeepAlive starts a goroutine that sends presence updates to keep the connection alive
func (c *Client) startKeepAlive() {
	ctx, cancel := context.WithCancel(context.Background())
	c.keepAliveCancel = cancel

	go func() {
		ticker := time.NewTicker(60 * time.Second)
		defer ticker.Stop()

		for {
			select {
			case <-ctx.Done():
				logging.Debug("Keep-alive stopped", "account_id", c.id)
				return
			case <-ticker.C:
				if c.client != nil && c.client.IsConnected() {
					err := c.client.SendPresence(ctx, types.PresenceAvailable)
					if err != nil {
						logging.Warn("Failed to send presence", "account_id", c.id, "error", err)
					} else {
						logging.Debug("Sent presence update", "account_id", c.id)
					}
				}
			}
		}
	}()

	logging.Info("Keep-alive started", "account_id", c.id)
}

// processMediaData processes media based on the configured mode
func (c *Client) processMediaData(messageID string, data []byte, mimeType string, mediaBase64 *string) (filename string, mediaURL string) {
	mode := c.mediaConfig.Mode

	// Generate filename
	ext := getExtensionFromMimeType(mimeType)
	hash := sha256.Sum256(data)
	hashStr := hex.EncodeToString(hash[:8])
	filename = fmt.Sprintf("%s_%s%s", messageID, hashStr, ext)

	// Handle base64 mode
	if mode == "base64" || mode == "both" {
		*mediaBase64 = base64.StdEncoding.EncodeToString(data)
	}

	// Handle link mode
	if mode == "link" || mode == "both" {
		// Save file to disk
		filePath, err := c.saveMediaFile(messageID, data, mimeType)
		if err != nil {
			logging.Error("Failed to save media file", "account_id", c.id, "message_id", messageID, "error", err)
		} else {
			// Extract just the filename from the full path
			filename = filepath.Base(filePath)
			mediaURL = c.generateMediaURL(messageID, filename)
		}
	}

	return filename, mediaURL
}

// saveMediaFile saves media data to disk and returns the file path
func (c *Client) saveMediaFile(messageID string, data []byte, mimeType string) (string, error) {
	// Create account-specific media directory
	mediaDir := filepath.Join(c.mediaConfig.DataPath, c.id)
	if err := os.MkdirAll(mediaDir, 0755); err != nil {
		return "", fmt.Errorf("failed to create media directory: %w", err)
	}

	// Generate unique filename using message ID and hash
	hash := sha256.Sum256(data)
	hashStr := hex.EncodeToString(hash[:8])
	ext := getExtensionFromMimeType(mimeType)
	filename := fmt.Sprintf("%s_%s%s", messageID, hashStr, ext)

	// Full path to file
	filePath := filepath.Join(mediaDir, filename)

	// Write file
	if err := os.WriteFile(filePath, data, 0644); err != nil {
		return "", fmt.Errorf("failed to write media file: %w", err)
	}

	return filePath, nil
}

// generateMediaURL generates a URL for accessing stored media
func (c *Client) generateMediaURL(messageID, filename string) string {
	baseURL := c.mediaConfig.BaseURL
	if baseURL == "" {
		baseURL = "http://localhost:8825"
	}
	return fmt.Sprintf("%s/api/media/%s/%s", baseURL, c.id, filename)
}

// getExtensionFromMimeType returns the file extension for a given MIME type
func getExtensionFromMimeType(mimeType string) string {
	extensions := map[string]string{
		// Images
		"image/jpeg":    ".jpg",
		"image/jpg":     ".jpg",
		"image/png":     ".png",
		"image/gif":     ".gif",
		"image/webp":    ".webp",
		"image/bmp":     ".bmp",
		"image/svg+xml": ".svg",

		// Videos
		"video/mp4":       ".mp4",
		"video/mpeg":      ".mpeg",
		"video/quicktime": ".mov",
		"video/x-msvideo": ".avi",
		"video/webm":      ".webm",
		"video/3gpp":      ".3gp",

		// Documents
		"application/pdf":    ".pdf",
		"application/msword": ".doc",
		"application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
		"application/vnd.ms-excel": ".xls",
		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":         ".xlsx",
		"application/vnd.ms-powerpoint":                                             ".ppt",
		"application/vnd.openxmlformats-officedocument.presentationml.presentation": ".pptx",
		"text/plain":                   ".txt",
		"text/html":                    ".html",
		"application/zip":              ".zip",
		"application/x-rar-compressed": ".rar",
		"application/x-7z-compressed":  ".7z",
		"application/json":             ".json",
		"application/xml":              ".xml",

		// Audio
		"audio/mpeg":  ".mp3",
		"audio/ogg":   ".ogg",
		"audio/wav":   ".wav",
		"audio/aac":   ".aac",
		"audio/x-m4a": ".m4a",
	}

	if ext, ok := extensions[mimeType]; ok {
		return ext
	}
	return ""
}

// generateQRCodeURL generates a URL for accessing the QR code
func (c *Client) generateQRCodeURL() string {
	baseURL := c.mediaConfig.BaseURL
	if baseURL == "" {
		baseURL = "http://localhost:8825"
	}
	return fmt.Sprintf("%s/api/qr/%s", baseURL, c.id)
}

// GetQRCodePNG returns the cached PNG image of the current QR code
func (c *Client) GetQRCodePNG() ([]byte, error) {
	c.qrCodeMutex.RLock()
	defer c.qrCodeMutex.RUnlock()

	if c.qrCode == "" {
		return nil, fmt.Errorf("no QR code available")
	}

	if c.qrCodePNG == nil {
		return nil, fmt.Errorf("QR code PNG not yet generated")
	}

	return c.qrCodePNG, nil
}

// generateQRCodePNG generates a PNG image from QR code data
func (c *Client) generateQRCodePNG(qrCodeData string) ([]byte, error) {
	// Generate QR code using rsc.io/qr
	code, err := qr.Encode(qrCodeData, qr.H)
	if err != nil {
		return nil, fmt.Errorf("failed to encode QR code: %w", err)
	}

	// Use the library's built-in PNG method with a scale factor
	// Scale of 8 means each QR module is 8x8 pixels
	return code.PNG(), nil
}
