package threadsapi

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

type MediaDetail struct {
	ID        string `json:"id"`
	Text      string `json:"text"`
	Username  string `json:"username"`
	Permalink string `json:"permalink"`
	Timestamp string `json:"timestamp"`
	MediaType string `json:"media_type"`
	IsReply   bool   `json:"is_reply"`
}

func (c *Client) GetMediaDetail(ctx context.Context, mediaID string) (*MediaDetail, 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")
	}
	params := url.Values{}
	params.Set("access_token", c.accessToken)
	params.Set("fields", "id,text,username,permalink,timestamp,media_type,is_reply")
	endpoint := graphBaseURL + "/" + url.PathEscape(mediaID) + "?" + params.Encode()
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
	if err != nil {
		return nil, err
	}
	body, status, err := doRequest(req)
	if err != nil {
		return nil, err
	}
	if status != http.StatusOK {
		return nil, parseAPIError(body, status)
	}
	var out MediaDetail
	if err := json.Unmarshal(body, &out); err != nil {
		return nil, fmt.Errorf("parse media detail: %w", err)
	}
	out.ID = strings.TrimSpace(out.ID)
	if out.ID == "" {
		return nil, fmt.Errorf("threads media detail missing id")
	}
	return &out, nil
}

type MediaStats struct {
	LikeCount   int `json:"like_count"`
	ReplyCount  int `json:"reply_count"`
	RepostCount int `json:"repost_count"`
	QuoteCount  int `json:"quote_count"`
	Views       int `json:"views,omitempty"`
	Shares      int `json:"shares,omitempty"`
}

// GetMediaStats loads engagement counts via GET /{media-id}/insights.
// Meta does not expose like_count on the media object itself (requesting those fields returns API errors).
func GetMediaStats(ctx context.Context, accessToken, mediaID string) (*MediaStats, error) {
	return NewClient(accessToken).GetMediaStats(ctx, mediaID)
}

func (c *Client) GetMediaStats(ctx context.Context, mediaID string) (*MediaStats, 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")
	}
	metrics, err := c.MediaInsights(ctx, mediaID, []string{"likes", "replies", "reposts", "quotes", "views", "shares"})
	if err != nil {
		if IsPermissionError(err) {
			return nil, fmt.Errorf("%w（貼文互動數需 threads_manage_insights，請確認 OAuth 已授權）", err)
		}
		return nil, err
	}
	return mapInsightMetrics(metrics), nil
}

func mapInsightMetrics(metrics map[string]int) *MediaStats {
	if metrics == nil {
		metrics = map[string]int{}
	}
	return &MediaStats{
		LikeCount:   metrics["likes"],
		ReplyCount:  metrics["replies"],
		RepostCount: metrics["reposts"],
		QuoteCount:  metrics["quotes"],
		Views:       metrics["views"],
		Shares:      metrics["shares"],
	}
}

type TokenRefreshResult struct {
	AccessToken string `json:"access_token"`
	TokenType   string `json:"token_type"`
	ExpiresIn   int    `json:"expires_in"`
}

func RefreshAccessToken(ctx context.Context, currentToken string) (*TokenRefreshResult, error) {
	u := fmt.Sprintf("%s/refresh_access_token", graphBaseURL)
	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
	if err != nil {
		return nil, err
	}
	q := url.Values{}
	q.Set("grant_type", "th_refresh_token")
	q.Set("access_token", currentToken)
	req.URL.RawQuery = q.Encode()

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("threads api token refresh error: %s", resp.Status)
	}

	var result TokenRefreshResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return nil, err
	}
	if result.AccessToken == "" {
		return nil, fmt.Errorf("threads api token refresh returned empty token")
	}
	return &result, nil
}

func NsFromNow(seconds int) int64 {
	return time.Now().UnixNano() + int64(seconds)*1e9
}
