// Package sender는 수집 서버 전송과 장애 대비 페이로드 버퍼를 제공합니다.
package sender

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

	"host-agent/internal/payload"
)

// Sender는 수집 서버로 페이로드를 POST 전송합니다.
type Sender struct {
	URL    string
	APIKey string
	Client *http.Client
}

func New(url, apiKey string) *Sender {
	return &Sender{URL: url, APIKey: apiKey, Client: &http.Client{Timeout: 8 * time.Second}}
}

// Send는 호스트 페이로드를 JSON으로 직렬화해 수집 서버로 POST 합니다.
func (s *Sender) Send(p payload.HostPayload) error {
	body, err := json.Marshal(p)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("POST", s.URL, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-API-Key", s.APIKey)
	resp, err := s.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("HTTP %d", resp.StatusCode)
	}
	return nil
}

// Buffer는 수집 서버 전송에 실패한 페이로드를 보관했다가
// 서버 복구 시 오래된 것부터 순서대로 재전송하기 위한 FIFO 버퍼입니다.
// 용량을 초과하면 가장 오래된 페이로드부터 버립니다.
type Buffer struct {
	items []payload.HostPayload
	max   int
}

func NewBuffer(max int) *Buffer {
	return &Buffer{max: max}
}

func (b *Buffer) Len() int { return len(b.items) }

// Push는 페이로드를 버퍼 끝에 추가하고, 용량 초과분은 앞(오래된 쪽)에서 버립니다.
func (b *Buffer) Push(p payload.HostPayload) {
	b.items = append(b.items, p)
	if over := len(b.items) - b.max; over > 0 {
		b.items = b.items[over:]
	}
}

// Flush는 오래된 것부터 send로 전송하며, 성공분은 제거하고 첫 실패에서 멈춥니다.
// 실패한 페이로드와 그 이후분은 다음 Flush를 위해 보관됩니다.
func (b *Buffer) Flush(send func(payload.HostPayload) error) error {
	for len(b.items) > 0 {
		if err := send(b.items[0]); err != nil {
			return err
		}
		b.items = b.items[1:]
	}
	return nil
}
