package weixin

import (
	"context"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"log/slog"
	"mime"
	"path/filepath"
	"strings"
	"time"

	"github.com/chenhg5/cc-connect/core"
)

func imageDecryptMaterial(img *imageItem) (encParam, aesKeyBase64 string, ok bool) {
	if img == nil || img.Media == nil {
		return "", "", false
	}
	encParam = strings.TrimSpace(img.Media.EncryptQueryParam)
	if encParam == "" {
		return "", "", false
	}
	if hx := strings.TrimSpace(img.AESKeyHex); hx != "" {
		raw, err := hex.DecodeString(hx)
		if err == nil && len(raw) == 16 {
			return encParam, base64.StdEncoding.EncodeToString(raw), true
		}
	}
	if k := strings.TrimSpace(img.Media.AESKey); k != "" {
		return encParam, k, true
	}
	return encParam, "", false
}

func (p *Platform) collectInboundMedia(ctx context.Context, items []messageItem) (images []core.ImageAttachment, files []core.FileAttachment, audio *core.AudioAttachment) {
	if p == nil || len(items) == 0 || strings.TrimSpace(p.cdnBaseURL) == "" {
		return nil, nil, nil
	}
	dlCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
	defer cancel()
	client := p.cdnHttpClient
	if client == nil {
		client = p.httpClient
	}
	base := p.cdnBaseURL

	// Deduplicate identical CDN references within one message (duplicate items / retries).
	seenEnc := make(map[string]struct{})
	tryEnc := func(enc string) bool {
		if enc == "" {
			return false
		}
		if _, ok := seenEnc[enc]; ok {
			return false
		}
		seenEnc[enc] = struct{}{}
		return true
	}
	var extraVoiceN int

	for _, it := range items {
		switch it.Type {
		case messageItemImage:
			img := it.ImageItem
			enc, keyB64, hasKey := imageDecryptMaterial(img)
			if enc == "" || !tryEnc(enc) {
				continue
			}
			var buf []byte
			var err error
			if hasKey && keyB64 != "" {
				buf, err = downloadAndDecryptCDN(dlCtx, client, base, enc, keyB64, "weixin inbound image")
			} else {
				buf, err = downloadPlainCDN(dlCtx, client, base, enc, "weixin inbound image-plain")
			}
			if err != nil {
				slog.Warn("weixin: inbound image CDN failed", "error", err)
				continue
			}
			mt := detectImageMime(buf)
			images = append(images, core.ImageAttachment{MimeType: mt, Data: buf})

		case messageItemFile:
			f := it.FileItem
			if f == nil || f.Media == nil {
				continue
			}
			enc := strings.TrimSpace(f.Media.EncryptQueryParam)
			keyB64 := strings.TrimSpace(f.Media.AESKey)
			if enc == "" || keyB64 == "" || !tryEnc(enc) {
				continue
			}
			buf, err := downloadAndDecryptCDN(dlCtx, client, base, enc, keyB64, "weixin inbound file")
			if err != nil {
				slog.Warn("weixin: inbound file CDN failed", "error", err)
				continue
			}
			name := strings.TrimSpace(f.FileName)
			if name == "" {
				name = "attachment.bin"
			}
			mt := mime.TypeByExtension(filepath.Ext(name))
			if mt == "" {
				mt = "application/octet-stream"
			}
			files = append(files, core.FileAttachment{MimeType: mt, Data: buf, FileName: name})

		case messageItemVideo:
			v := it.VideoItem
			if v == nil || v.Media == nil {
				continue
			}
			enc := strings.TrimSpace(v.Media.EncryptQueryParam)
			keyB64 := strings.TrimSpace(v.Media.AESKey)
			if enc == "" || keyB64 == "" || !tryEnc(enc) {
				continue
			}
			buf, err := downloadAndDecryptCDN(dlCtx, client, base, enc, keyB64, "weixin inbound video")
			if err != nil {
				slog.Warn("weixin: inbound video CDN failed", "error", err)
				continue
			}
			files = append(files, core.FileAttachment{MimeType: "video/mp4", Data: buf, FileName: "video.mp4"})

		case messageItemVoice:
			v := it.VoiceItem
			if v == nil || v.Media == nil {
				continue
			}
			if strings.TrimSpace(v.Text) != "" {
				// WeChat ASR text is enough when present; avoid STT path / duplicate handling.
				continue
			}
			enc := strings.TrimSpace(v.Media.EncryptQueryParam)
			keyB64 := strings.TrimSpace(v.Media.AESKey)
			if enc == "" || keyB64 == "" || !tryEnc(enc) {
				continue
			}
			buf, err := downloadAndDecryptCDN(dlCtx, client, base, enc, keyB64, "weixin inbound voice")
			if err != nil {
				slog.Warn("weixin: inbound voice CDN failed", "error", err)
				continue
			}
			a := &core.AudioAttachment{
				MimeType: "audio/silk",
				Data:     buf,
				Format:   "silk",
			}
			if audio == nil {
				audio = a
			} else {
				// core.Message carries one Audio; extra raw voice segments go as file attachments for the agent.
				extraVoiceN++
				files = append(files, core.FileAttachment{
					MimeType: "audio/silk",
					Data:     buf,
					FileName: fmt.Sprintf("voice_%d.silk", extraVoiceN),
				})
			}
		}
	}
	return images, files, audio
}
