package main

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

type APIClient struct {
	baseURL    string
	secret     string
	httpClient *http.Client
}

func NewAPIClient(baseURL, secret string) *APIClient {
	return &APIClient{
		baseURL: baseURL,
		secret:  secret,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// NotifyMatchEvent informs the backend of orchestrator-side state changes
// (e.g. server ready, crashed) that don't originate from MatchZy webhooks.
func (c *APIClient) NotifyMatchEvent(matchID string, eventType string, payload map[string]interface{}) error {
	body := map[string]interface{}{
		"match_id":   matchID,
		"event_type": eventType,
		"payload":    payload,
		"timestamp":  time.Now().UTC().Format(time.RFC3339),
	}

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

	url := fmt.Sprintf("%s/api/internal/match-events", c.baseURL)
	req, err := http.NewRequest("POST", url, bytes.NewReader(jsonBody))
	if err != nil {
		return fmt.Errorf("create request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+c.secret)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("send event request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("API returned %d: %s", resp.StatusCode, string(respBody))
	}

	return nil
}

func (c *APIClient) NotifyServerReady(matchID string, connectString string) error {
	return c.NotifyMatchEvent(matchID, "server_ready", map[string]interface{}{
		"connect_string": connectString,
	})
}

func (c *APIClient) NotifyServerCrash(matchID string, reason string) error {
	return c.NotifyMatchEvent(matchID, "server_crash", map[string]interface{}{
		"reason": reason,
	})
}

func (c *APIClient) NotifyBootTimeout(matchID string) error {
	return c.NotifyMatchEvent(matchID, "boot_timeout", map[string]interface{}{})
}

func (c *APIClient) HealthCheck() error {
	url := fmt.Sprintf("%s/api/health", c.baseURL)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("create health check request: %w", err)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return fmt.Errorf("health check request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("backend API returned status %d", resp.StatusCode)
	}

	return nil
}

// FetchMatchConfig is for debugging only, MatchZy normally fetches config directly.
func (c *APIClient) FetchMatchConfig(matchID string) (map[string]interface{}, error) {
	url := fmt.Sprintf("%s/api/internal/match-config/%s", c.baseURL, matchID)
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("create config request: %w", err)
	}

	req.Header.Set("Authorization", "Bearer "+c.secret)

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("fetch config request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("API returned %d: %s", resp.StatusCode, string(respBody))
	}

	var config map[string]interface{}
	if err := json.NewDecoder(resp.Body).Decode(&config); err != nil {
		return nil, fmt.Errorf("decode config response: %w", err)
	}

	return config, nil
}

func logAPIError(operation string, err error) {
	if err != nil {
		log.Printf("[api] %s failed: %v", operation, err)
	}
}
