package threadsapi

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
)

const mediaConversationFields = "id,text,username,permalink,timestamp,is_reply,is_reply_owned_by_me,has_replies,replied_to,root_post,parent_id,media_type,media_url,thumbnail_url,gif_url,children{id,media_type,media_url,thumbnail_url}"

type ReplyMediaChild struct {
	ID           string `json:"id"`
	MediaType    string `json:"media_type"`
	MediaURL     string `json:"media_url"`
	ThumbnailURL string `json:"thumbnail_url"`
}

type ReplyItem struct {
	ID               string            `json:"id"`
	Text             string            `json:"text"`
	Username         string            `json:"username"`
	Permalink        string            `json:"permalink"`
	Timestamp        string            `json:"timestamp"`
	LikeCount        int               `json:"like_count"`
	ParentID         string            `json:"parent_id"`
	RepliedToID      string            `json:"replied_to_id"`
	RootPostID       string            `json:"root_post_id"`
	IsReply          bool              `json:"is_reply"`
	IsReplyOwnedByMe bool              `json:"is_reply_owned_by_me"`
	HasReplies       bool              `json:"has_replies"`
	MediaType        string            `json:"media_type"`
	MediaURL         string            `json:"media_url"`
	ThumbnailURL     string            `json:"thumbnail_url"`
	GifURL           string            `json:"gif_url"`
	MediaChildren    []ReplyMediaChild `json:"media_children,omitempty"`
}

type replyItemRaw struct {
	ID               string          `json:"id"`
	Text             string          `json:"text"`
	Username         string          `json:"username"`
	Permalink        string          `json:"permalink"`
	Timestamp        string          `json:"timestamp"`
	LikeCount        int             `json:"like_count"`
	ParentID         string          `json:"parent_id"`
	RepliedTo        json.RawMessage `json:"replied_to"`
	RootPost         json.RawMessage `json:"root_post"`
	IsReply          bool            `json:"is_reply"`
	IsReplyOwnedByMe bool            `json:"is_reply_owned_by_me"`
	HasReplies       bool            `json:"has_replies"`
	MediaType        string          `json:"media_type"`
	MediaURL         string          `json:"media_url"`
	ThumbnailURL     string          `json:"thumbnail_url"`
	GifURL           string          `json:"gif_url"`
	Children         json.RawMessage `json:"children"`
}

func (c *Client) MediaReplies(ctx context.Context, mediaID string, limit int) ([]ReplyItem, error) {
	return c.MediaConversation(ctx, mediaID, limit)
}

// MediaConversation returns flattened top-level and nested replies for a thread root.
func (c *Client) MediaConversation(ctx context.Context, mediaID string, limit int) ([]ReplyItem, error) {
	if !c.Enabled() {
		return nil, fmt.Errorf("threads api access token is required")
	}
	mediaID = strings.TrimSpace(mediaID)
	if mediaID == "" {
		return nil, fmt.Errorf("threads media id is required")
	}
	if limit <= 0 {
		limit = 10
	}
	if limit > 100 {
		limit = 100
	}

	out := make([]ReplyItem, 0, limit)
	nextURL := ""
	for len(out) < limit {
		pageLimit := limit - len(out)
		if pageLimit > 50 {
			pageLimit = 50
		}
		endpoint := nextURL
		if endpoint == "" {
			params := url.Values{}
			params.Set("access_token", c.accessToken)
			params.Set("fields", mediaConversationFields)
			params.Set("limit", fmt.Sprintf("%d", pageLimit))
			params.Set("reverse", "false")
			endpoint = graphBaseURL + "/" + url.PathEscape(mediaID) + "/conversation?" + params.Encode()
		}

		req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
		if err != nil {
			return nil, err
		}
		res, err := c.http.Do(req)
		if err != nil {
			return nil, err
		}

		body, err := io.ReadAll(io.LimitReader(res.Body, 1<<20))
		res.Body.Close()
		if err != nil {
			return nil, err
		}
		if res.StatusCode != http.StatusOK {
			return nil, parseAPIError(body, res.StatusCode)
		}

		var payload struct {
			Data   []replyItemRaw `json:"data"`
			Paging struct {
				Next string `json:"next"`
			} `json:"paging"`
		}
		if err := json.Unmarshal(body, &payload); err != nil {
			return nil, fmt.Errorf("parse threads conversation: %w", err)
		}
		for _, raw := range payload.Data {
			out = append(out, normalizeReplyItem(raw))
			if len(out) >= limit {
				break
			}
		}
		nextURL = strings.TrimSpace(payload.Paging.Next)
		if nextURL == "" || len(payload.Data) == 0 {
			break
		}
	}
	return out, nil
}

func normalizeReplyItem(raw replyItemRaw) ReplyItem {
	repliedToID := parseMediaRefID(raw.RepliedTo)
	if repliedToID == "" {
		repliedToID = strings.TrimSpace(raw.ParentID)
	}
	return ReplyItem{
		ID:               strings.TrimSpace(raw.ID),
		Text:             strings.TrimSpace(raw.Text),
		Username:         strings.TrimSpace(raw.Username),
		Permalink:        strings.TrimSpace(raw.Permalink),
		Timestamp:        strings.TrimSpace(raw.Timestamp),
		LikeCount:        raw.LikeCount,
		ParentID:         strings.TrimSpace(raw.ParentID),
		RepliedToID:      repliedToID,
		RootPostID:       parseMediaRefID(raw.RootPost),
		IsReply:          raw.IsReply,
		IsReplyOwnedByMe: raw.IsReplyOwnedByMe,
		HasReplies:       raw.HasReplies,
		MediaType:        strings.TrimSpace(raw.MediaType),
		MediaURL:         strings.TrimSpace(raw.MediaURL),
		ThumbnailURL:     strings.TrimSpace(raw.ThumbnailURL),
		GifURL:           strings.TrimSpace(raw.GifURL),
		MediaChildren:    parseMediaChildren(raw.Children),
	}
}

func parseMediaChildren(raw json.RawMessage) []ReplyMediaChild {
	if len(raw) == 0 || string(raw) == "null" {
		return nil
	}

	var asList []ReplyMediaChild
	if err := json.Unmarshal(raw, &asList); err == nil {
		return normalizeMediaChildren(asList)
	}

	var wrapped struct {
		Data []ReplyMediaChild `json:"data"`
	}
	if err := json.Unmarshal(raw, &wrapped); err == nil && len(wrapped.Data) > 0 {
		return normalizeMediaChildren(wrapped.Data)
	}
	return nil
}

func normalizeMediaChildren(items []ReplyMediaChild) []ReplyMediaChild {
	out := make([]ReplyMediaChild, 0, len(items))
	for _, item := range items {
		child := ReplyMediaChild{
			ID:           strings.TrimSpace(item.ID),
			MediaType:    strings.TrimSpace(item.MediaType),
			MediaURL:     strings.TrimSpace(item.MediaURL),
			ThumbnailURL: strings.TrimSpace(item.ThumbnailURL),
		}
		if child.ID == "" && child.MediaURL == "" && child.ThumbnailURL == "" {
			continue
		}
		out = append(out, child)
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func parseMediaRefID(raw json.RawMessage) string {
	if len(raw) == 0 {
		return ""
	}
	var asString string
	if err := json.Unmarshal(raw, &asString); err == nil {
		return strings.TrimSpace(asString)
	}
	var asObject struct {
		ID string `json:"id"`
	}
	if err := json.Unmarshal(raw, &asObject); err == nil {
		return strings.TrimSpace(asObject.ID)
	}
	return ""
}
