package businessapi

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

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

	"go.mau.fi/whatsmeow/types"
)

var errNoBusinessAccount = errors.New("business_account_id is required for this operation")

// SendAudio sends an audio message via Business API.
func (c *Client) SendAudio(ctx context.Context, jid types.JID, audioData []byte, mimeType string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	mediaID, err := c.uploadMedia(ctx, audioData, mimeType)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", fmt.Errorf("failed to upload audio: %w", err)
	}

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "audio",
		Audio:            &MediaObject{ID: mediaID},
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Audio sent via Business API", "account_id", c.id, "to", phoneNumber)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, ""))
	return messageID, nil
}

// SendSticker sends a sticker message via Business API.
func (c *Client) SendSticker(ctx context.Context, jid types.JID, stickerData []byte, mimeType string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	mediaID, err := c.uploadMedia(ctx, stickerData, mimeType)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", fmt.Errorf("failed to upload sticker: %w", err)
	}

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "sticker",
		Sticker:          &MediaObject{ID: mediaID},
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Sticker sent via Business API", "account_id", c.id, "to", phoneNumber)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, ""))
	return messageID, nil
}

// SendLocation sends a location message via Business API.
func (c *Client) SendLocation(ctx context.Context, jid types.JID, latitude, longitude float64, name, address string) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "location",
		Location: &LocationObject{
			Latitude:  latitude,
			Longitude: longitude,
			Name:      name,
			Address:   address,
		},
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Location sent via Business API", "account_id", c.id, "to", phoneNumber)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, fmt.Sprintf("%.6f,%.6f", latitude, longitude)))
	return messageID, nil
}

// SendContacts sends one or more contact cards.
func (c *Client) SendContacts(ctx context.Context, jid types.JID, contacts []SendContactObject) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "contacts",
		Contacts:         contacts,
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Contacts sent via Business API", "account_id", c.id, "to", phoneNumber)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, ""))
	return messageID, nil
}

// SendInteractive sends an interactive message (buttons, list, or flow).
func (c *Client) SendInteractive(ctx context.Context, jid types.JID, interactive *InteractiveObject) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "interactive",
		Interactive:      interactive,
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Interactive message sent via Business API", "account_id", c.id, "to", phoneNumber, "type", interactive.Type)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, ""))
	return messageID, nil
}

// SendTemplateMessage sends a template message.
func (c *Client) SendTemplateMessage(ctx context.Context, jid types.JID, tmpl *TemplateMessageObject) (string, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	reqBody := SendMessageRequest{
		MessagingProduct: "whatsapp",
		To:               phoneNumber,
		Type:             "template",
		Template:         tmpl,
	}

	messageID, err := c.sendMessage(ctx, reqBody)
	if err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, "", err))
		return "", err
	}

	logging.Debug("Template message sent via Business API", "account_id", c.id, "to", phoneNumber, "template", tmpl.Name)
	c.eventBus.Publish(events.MessageSentEvent(ctx, c.id, messageID, phoneNumber, tmpl.Name))
	return messageID, nil
}

// SendReaction sends a reaction (emoji) to an existing message.
func (c *Client) SendReaction(ctx context.Context, jid types.JID, messageID string, emoji string) error {
	if ctx == nil {
		ctx = context.Background()
	}
	phoneNumber := jidToPhoneNumber(jid)

	msg := ReactionMessage{
		MessagingProduct: "whatsapp",
		MessageType:      "reaction",
		Reaction: ReactionObject{
			MessageID: messageID,
			Emoji:     emoji,
		},
	}

	if _, err := c.postToMessagesEndpoint(ctx, msg); err != nil {
		c.eventBus.Publish(events.MessageFailedEvent(ctx, c.id, phoneNumber, emoji, err))
		return err
	}

	logging.Debug("Reaction sent via Business API", "account_id", c.id, "to", phoneNumber, "emoji", emoji)
	return nil
}

// MarkAsRead marks a received message as read.
func (c *Client) MarkAsRead(ctx context.Context, messageID string) error {
	if ctx == nil {
		ctx = context.Background()
	}

	msg := ReadReceiptMessage{
		MessagingProduct: "whatsapp",
		Status:           "read",
		MessageID:        messageID,
	}

	if _, err := c.postToMessagesEndpoint(ctx, msg); err != nil {
		return err
	}

	logging.Debug("Message marked as read via Business API", "account_id", c.id, "message_id", messageID)
	return nil
}

// ListPhoneNumbers returns all phone numbers for the WhatsApp Business Account.
// The WABA ID is resolved from the phone number at connect time; it is distinct from
// the Facebook Business Manager ID stored in business_account_id.
func (c *Client) ListPhoneNumbers(ctx context.Context) (*PhoneNumberListResponse, error) {
	wabaID := c.wabaID
	if wabaID == "" {
		// Fallback: resolve on demand if Connect() was not called
		id, err := c.fetchWABAID(ctx)
		if err != nil {
			return nil, fmt.Errorf("could not resolve WABA ID: %w", err)
		}
		c.wabaID = id
		wabaID = id
	}

	params := url.Values{
		"fields": {"id,display_phone_number,verified_name,code_verification_status,quality_rating,throughput"},
	}

	var resp PhoneNumberListResponse
	if err := c.graphAPIGet(ctx, wabaID+"/phone_numbers", params, &resp); err != nil {
		return nil, err
	}
	return &resp, nil
}

// RequestVerificationCode sends a verification code (SMS or VOICE) to the given phone number.
func (c *Client) RequestVerificationCode(ctx context.Context, phoneNumberID string, method string, language string) error {
	body := map[string]string{
		"code_method": method,
		"language":    language,
	}

	var resp RequestCodeResponse
	return c.graphAPIPost(ctx, phoneNumberID+"/request_code", body, &resp)
}

// VerifyCode verifies a phone number with the code received via SMS/VOICE.
func (c *Client) VerifyCode(ctx context.Context, phoneNumberID string, code string) error {
	body := VerifyCodeRequest{Code: code}

	var resp VerifyCodeResponse
	return c.graphAPIPost(ctx, phoneNumberID+"/verify_code", body, &resp)
}

// RegisterPhoneNumber registers a phone number for use with the WhatsApp Cloud API.
// POST /{phone-number-id}/register
func (c *Client) RegisterPhoneNumber(ctx context.Context, phoneNumberID string, pin string) error {
	body := RegisterPhoneNumberRequest{
		MessagingProduct: "whatsapp",
		Pin:              pin,
	}
	var resp RegisterPhoneNumberResponse
	return c.graphAPIPost(ctx, phoneNumberID+"/register", body, &resp)
}

// DeleteMedia deletes a previously uploaded media file.
func (c *Client) DeleteMedia(ctx context.Context, mediaID string) error {
	return c.graphAPIDelete(ctx, mediaID, nil)
}
