package telegram

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)

// httpClient is a shared client with sensible timeouts for Telegram Bot API calls.
var httpClient = &http.Client{
	Timeout: 10 * time.Second,
}

// apiResponse is the generic envelope returned by every Bot API method.
type apiResponse struct {
	OK          bool            `json:"ok"`
	Result      json.RawMessage `json:"result,omitempty"`
	Description string          `json:"description,omitempty"`
	ErrorCode   int             `json:"error_code,omitempty"`
}

// CallBotAPI sends a JSON request to the Telegram Bot API and returns the result field.
func CallBotAPI(botToken, method string, payload any) (json.RawMessage, error) {
	url := fmt.Sprintf("https://api.telegram.org/bot%s/%s", botToken, method)

	body, err := json.Marshal(payload)
	if err != nil {
		return nil, fmt.Errorf("telegram: marshal payload: %w", err)
	}

	resp, err := httpClient.Post(url, "application/json", bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("telegram: POST %s: %w", method, err)
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("telegram: read response: %w", err)
	}

	var apiResp apiResponse
	if err := json.Unmarshal(respBody, &apiResp); err != nil {
		return nil, fmt.Errorf("telegram: unmarshal response: %w", err)
	}

	if !apiResp.OK {
		return nil, fmt.Errorf("telegram: %s failed (%d): %s", method, apiResp.ErrorCode, apiResp.Description)
	}

	return apiResp.Result, nil
}

// LabeledAmount represents one price component in a Telegram invoice.
type LabeledAmount struct {
	Label  string `json:"label"`
	Amount int    `json:"amount"` // smallest currency unit (kopecks for RUB)
}

// InvoiceLinkParams holds the parameters for createInvoiceLink.
type InvoiceLinkParams struct {
	Title         string          `json:"title"`
	Description   string          `json:"description"`
	Payload       string          `json:"payload"`
	ProviderToken string          `json:"provider_token"`
	Currency      string          `json:"currency"`
	Prices        []LabeledAmount `json:"prices"`
}

// CreateInvoiceLink calls the Telegram Bot API createInvoiceLink method
// and returns the HTTPS invoice URL the client can pass to openInvoice().
func CreateInvoiceLink(botToken string, params InvoiceLinkParams) (string, error) {
	raw, err := CallBotAPI(botToken, "createInvoiceLink", params)
	if err != nil {
		return "", err
	}

	var link string
	if err := json.Unmarshal(raw, &link); err != nil {
		return "", fmt.Errorf("telegram: unmarshal invoice link: %w", err)
	}
	return link, nil
}

// AnswerPreCheckoutQuery responds to a Telegram pre_checkout_query.
// ok=true approves; ok=false + errorMsg declines.
func AnswerPreCheckoutQuery(botToken, queryID string, ok bool, errorMsg string) error {
	payload := map[string]any{
		"pre_checkout_query_id": queryID,
		"ok":                    ok,
	}
	if !ok && errorMsg != "" {
		payload["error_message"] = errorMsg
	}

	_, err := CallBotAPI(botToken, "answerPreCheckoutQuery", payload)
	return err
}

// SetWebhook configures the Telegram webhook URL for payment callbacks.
func SetWebhook(botToken, webhookURL string) error {
	payload := map[string]string{
		"url": webhookURL,
	}
	_, err := CallBotAPI(botToken, "setWebhook", payload)
	return err
}
