package threadsapi

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

const graphBaseURL = "https://graph.threads.net/v1.0"

type Client struct {
	accessToken string
	http        *http.Client
}

func NewClient(accessToken string) *Client {
	return &Client{
		accessToken: strings.TrimSpace(accessToken),
		http: &http.Client{
			Timeout: 25 * time.Second,
		},
	}
}

func (c *Client) Enabled() bool {
	return c != nil && c.accessToken != ""
}

type SearchItem struct {
	ID         string `json:"id"`
	Text       string `json:"text"`
	Permalink  string `json:"permalink"`
	Username   string `json:"username"`
	Timestamp  string `json:"timestamp"`
	LikeCount  int    `json:"like_count"`
	ReplyCount int    `json:"reply_count"`
}

type KeywordSearchOptions struct {
	Query      string
	Limit      int
	SearchType string // TOP or RECENT
}

func (c *Client) KeywordSearch(ctx context.Context, opts KeywordSearchOptions) ([]SearchItem, error) {
	if !c.Enabled() {
		return nil, fmt.Errorf("threads api access token is required")
	}
	query := strings.TrimSpace(opts.Query)
	if query == "" {
		return nil, fmt.Errorf("threads keyword search query is required")
	}
	searchType := strings.TrimSpace(opts.SearchType)
	if searchType == "" {
		searchType = "TOP"
	}
	limit := opts.Limit
	if limit <= 0 {
		limit = 12
	}
	if limit > 50 {
		limit = 50
	}

	params := url.Values{}
	params.Set("access_token", c.accessToken)
	params.Set("q", query)
	params.Set("search_type", searchType)
	params.Set("fields", "id,text,permalink,username,timestamp,like_count,reply_count,repost_count,quote_count")
	params.Set("limit", fmt.Sprintf("%d", limit))

	endpoint := graphBaseURL + "/keyword_search?" + 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
	}
	defer res.Body.Close()

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

	var payload struct {
		Data []SearchItem `json:"data"`
	}
	if err := json.Unmarshal(body, &payload); err != nil {
		return nil, fmt.Errorf("parse threads keyword_search: %w", err)
	}
	return payload.Data, nil
}

func parseAPIError(body []byte, status int) error {
	var payload struct {
		Error struct {
			Message string `json:"message"`
		} `json:"error"`
		ErrorMessage string `json:"error_message"`
	}
	_ = json.Unmarshal(body, &payload)
	msg := strings.TrimSpace(payload.Error.Message)
	if msg == "" {
		msg = strings.TrimSpace(payload.ErrorMessage)
	}
	if msg == "" {
		msg = string(body)
	}
	if len(msg) > 240 {
		msg = msg[:240]
	}
	return fmt.Errorf("threads api %d: %s", status, msg)
}
