package telegram

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	notconfig "github.com/tech/sendico/notification/interface/services/notification/config"
	"github.com/tech/sendico/pkg/merrors"
	"github.com/tech/sendico/pkg/mlogger"
	"github.com/tech/sendico/pkg/model"
)

const defaultAPIURL = "https://api.telegram.org"

type Client interface {
	SendDemoRequest(ctx context.Context, request *model.DemoRequest) error
}

type client struct {
	logger     mlogger.Logger
	httpClient *http.Client
	apiURL     string
	botToken   string
	chatID     string
	threadID   *int64
	parseMode  string
}

type sendMessagePayload struct {
	ChatID         string `json:"chat_id"`
	Text           string `json:"text"`
	ParseMode      string `json:"parse_mode,omitempty"`
	ThreadID       *int64 `json:"message_thread_id,omitempty"`
	DisablePreview bool   `json:"disable_web_page_preview,omitempty"`
	DisableNotify  bool   `json:"disable_notification,omitempty"`
	ProtectContent bool   `json:"protect_content,omitempty"`
}

func NewClient(logger mlogger.Logger, cfg *notconfig.TelegramConfig) (Client, error) {
	if cfg == nil {
		return nil, merrors.InvalidArgument("telegram configuration is not provided")
	}
	token := strings.TrimSpace(os.Getenv(cfg.BotTokenEnv))
	if token == "" {
		return nil, merrors.InvalidArgument(fmt.Sprintf("telegram bot token env %s is empty", cfg.BotTokenEnv))
	}
	chatID := strings.TrimSpace(os.Getenv(cfg.ChatIDEnv))
	if chatID == "" {
		return nil, merrors.InvalidArgument(fmt.Sprintf("telegram chat id env %s is empty", cfg.ChatIDEnv))
	}

	var threadID *int64
	if env := strings.TrimSpace(cfg.ThreadIDEnv); env != "" {
		raw := strings.TrimSpace(os.Getenv(env))
		if raw != "" {
			val, err := strconv.ParseInt(raw, 10, 64)
			if err != nil {
				return nil, merrors.InvalidArgumentWrap(err, fmt.Sprintf("telegram thread id env %s is invalid", env))
			}
			threadID = &val
		}
	}

	timeout := time.Duration(cfg.TimeoutSeconds) * time.Second
	if timeout <= 0 {
		timeout = 10 * time.Second
	}

	apiURL := strings.TrimSpace(cfg.APIURL)
	if apiURL == "" {
		apiURL = defaultAPIURL
	}

	return &client{
		logger: logger.Named("telegram"),
		httpClient: &http.Client{
			Timeout: timeout,
		},
		apiURL:    strings.TrimRight(apiURL, "/"),
		botToken:  token,
		chatID:    chatID,
		threadID:  threadID,
		parseMode: strings.TrimSpace(cfg.ParseMode),
	}, nil
}

func (c *client) SendDemoRequest(ctx context.Context, request *model.DemoRequest) error {
	if request == nil {
		return merrors.InvalidArgument("demo request payload is nil")
	}
	message := buildMessage(request)
	payload := sendMessagePayload{
		ChatID:         c.chatID,
		Text:           message,
		ParseMode:      c.parseMode,
		ThreadID:       c.threadID,
		DisablePreview: true,
	}
	return c.sendMessage(ctx, payload)
}

func (c *client) sendMessage(ctx context.Context, payload sendMessagePayload) error {
	body, err := json.Marshal(&payload)
	if err != nil {
		return err
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.endpoint(), bytes.NewReader(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

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

	if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices {
		return nil
	}
	respBody, _ := io.ReadAll(io.LimitReader(resp.Body, 4<<10))
	return merrors.Internal(fmt.Sprintf("telegram sendMessage failed with status %d: %s", resp.StatusCode, string(respBody)))
}

func (c *client) endpoint() string {
	return fmt.Sprintf("%s/bot%s/sendMessage", c.apiURL, c.botToken)
}

func buildMessage(req *model.DemoRequest) string {
	var builder strings.Builder
	builder.WriteString("New demo request received\n")
	builder.WriteString(fmt.Sprintf("Name: %s\n", req.Name))
	builder.WriteString(fmt.Sprintf("Organization: %s\n", req.OrganizationName))
	builder.WriteString(fmt.Sprintf("Phone: %s\n", req.Phone))
	builder.WriteString(fmt.Sprintf("Work email: %s\n", req.WorkEmail))
	builder.WriteString(fmt.Sprintf("Payout volume: %s\n", req.PayoutVolume))
	if req.Comment != "" {
		builder.WriteString(fmt.Sprintf("Comment: %s\n", req.Comment))
	}
	return builder.String()
}
