package services

import (
	"context"
	"fmt"
	"net/url"
	"regexp"
	"strings"
	"time"
	"unicode/utf8"

	"go.mau.fi/whatsmeow"
	waProto "go.mau.fi/whatsmeow/proto/waE2E"
	"go.mau.fi/whatsmeow/types"
	"google.golang.org/protobuf/proto"
)

// A send error can occur after WhatsApp has accepted the message. Never retry
// such a status automatically; the operator must inspect WhatsApp first.
type StatusSendUncertainError struct{ Err error }

func (e *StatusSendUncertainError) Error() string {
	return "hasil pengiriman belum dapat dipastikan; periksa Status WhatsApp sebelum mengirim ulang"
}
func (e *StatusSendUncertainError) Unwrap() error { return e.Err }

type statusWAClient interface {
	IsConnected() bool
	Upload(context.Context, []byte, whatsmeow.MediaType) (whatsmeow.UploadResponse, error)
	SendMessage(context.Context, types.JID, *waProto.Message, ...whatsmeow.SendRequestExtra) (whatsmeow.SendResponse, error)
}

func (w *waInstance) PostStatus(text, mimetype string, media []byte) error {
	return w.PostStatusContext(context.Background(), text, mimetype, media)
}

func (w *waInstance) PostStatusContext(ctx context.Context, text, mimetype string, media []byte) error {
	w.mu.Lock()
	client := w.client
	w.mu.Unlock()
	if client == nil {
		return fmt.Errorf("WhatsApp belum tersambung")
	}
	ctx, cancel := context.WithTimeout(ctx, 90*time.Second)
	defer cancel()
	return postStatusWithClient(ctx, client, text, mimetype, media)
}

func postStatusWithClient(ctx context.Context, client statusWAClient, text, mimetype string, media []byte) error {
	if err := ctx.Err(); err != nil {
		return err
	}
	if !client.IsConnected() {
		return fmt.Errorf("WhatsApp belum tersambung")
	}
	limit := StatusTextMaxChars
	if len(media) > 0 {
		limit = StatusCaptionMaxChars
	}
	if utf8.RuneCountInString(text) > limit {
		return fmt.Errorf("teks maksimal %d karakter", limit)
	}
	var msg *waProto.Message
	if len(media) == 0 {
		if mimetype != "" {
			return fmt.Errorf("lampiran status tidak tersedia; unggah ulang media")
		}
		if strings.TrimSpace(text) == "" {
			return fmt.Errorf("status tidak boleh kosong")
		}
		msg = &waProto.Message{ExtendedTextMessage: &waProto.ExtendedTextMessage{
			Text: proto.String(text), TextArgb: proto.Uint32(0xFFFFFFFF), BackgroundArgb: proto.Uint32(0xFF128C7E),
			Font: waProto.ExtendedTextMessage_SYSTEM.Enum(),
		}}
		if link := firstStatusLink(text); link != "" {
			msg.ExtendedTextMessage.MatchedText = proto.String(link)
		}
	} else {
		info, err := InspectStatusMedia(media)
		if err != nil {
			return err
		}
		if mimetype != "" && mimetype != info.Mimetype {
			return fmt.Errorf("jenis lampiran tidak sesuai isinya; unggah ulang media")
		}
		kind := whatsmeow.MediaImage
		if info.Kind == "video" {
			kind = whatsmeow.MediaVideo
		}
		up, err := client.Upload(ctx, media, kind)
		if err != nil {
			return fmt.Errorf("unggah media status gagal: %w", err)
		}
		if info.Kind == "video" {
			msg = &waProto.Message{VideoMessage: &waProto.VideoMessage{
				Caption: proto.String(text), Mimetype: proto.String(info.Mimetype), Seconds: proto.Uint32(info.Seconds),
				Width: proto.Uint32(info.Width), Height: proto.Uint32(info.Height),
				URL: proto.String(up.URL), DirectPath: proto.String(up.DirectPath), MediaKey: up.MediaKey,
				FileEncSHA256: up.FileEncSHA256, FileSHA256: up.FileSHA256, FileLength: proto.Uint64(up.FileLength),
			}}
		} else {
			msg = &waProto.Message{ImageMessage: &waProto.ImageMessage{
				Caption: proto.String(text), Mimetype: proto.String(info.Mimetype), JPEGThumbnail: info.Thumbnail,
				Width: proto.Uint32(info.Width), Height: proto.Uint32(info.Height),
				URL: proto.String(up.URL), DirectPath: proto.String(up.DirectPath), MediaKey: up.MediaKey,
				FileEncSHA256: up.FileEncSHA256, FileSHA256: up.FileSHA256, FileLength: proto.Uint64(up.FileLength),
			}}
		}
	}
	if err := ctx.Err(); err != nil {
		return err
	}
	_, err := client.SendMessage(ctx, types.StatusBroadcastJID, msg)
	if err != nil {
		return &StatusSendUncertainError{Err: err}
	}
	return nil
}

var statusLinkPattern = regexp.MustCompile(`(?i)https?://[^\s<>]+`)

func firstStatusLink(text string) string {
	for _, raw := range statusLinkPattern.FindAllString(text, -1) {
		link := strings.TrimRight(raw, ".,;!?)\"'")
		u, err := url.Parse(link)
		if err == nil && u.Hostname() != "" && u.User == nil && (strings.EqualFold(u.Scheme, "https") || strings.EqualFold(u.Scheme, "http")) {
			return link
		}
	}
	return ""
}
