package verify

import (
	"bytes"
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math/big"
	"net/http"
)

// Client handles verification with the hub
type Client struct {
	verifyURL string
	apiKey    string
	client    *http.Client
	verbose   bool
}

// NewClient creates a new verify client
func NewClient(verifyURL, apiKey string, verbose bool) *Client {
	return &Client{
		verifyURL: verifyURL,
		apiKey:    apiKey,
		client:    &http.Client{},
		verbose:   verbose,
	}
}

// VerifyRequest represents the request to the verify endpoint
type VerifyRequest struct {
	APIKey string       `json:"apikey"`
	Task   string       `json:"task"`
	Answer VerifyAnswer `json:"answer"`
}

// VerifyAnswer represents the answer part of verify request
type VerifyAnswer struct {
	URL       string `json:"url"`
	SessionID string `json:"sessionID"`
}

// VerifyResponse represents the response from verify endpoint
type VerifyResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Error   string `json:"error,omitempty"`
}

// Register registers the proxy with the hub
func (c *Client) Register(ctx context.Context, tunnelURL, sessionID string) error {
	reqBody := VerifyRequest{
		APIKey: c.apiKey,
		Task:   "proxy",
		Answer: VerifyAnswer{
			URL:       tunnelURL,
			SessionID: sessionID,
		},
	}

	jsonData, err := json.Marshal(reqBody)
	if err != nil {
		return fmt.Errorf("marshaling request: %w", err)
	}

	if c.verbose {
		var prettyJSON bytes.Buffer
		if err := json.Indent(&prettyJSON, jsonData, "", "  "); err == nil {
			log.Printf("\n========== VERIFY REQUEST ==========\nURL: %s\nBody:\n%s\n====================================\n", c.verifyURL, prettyJSON.String())
		}
	}

	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.verifyURL, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("creating request: %w", err)
	}

	req.Header.Set("Content-Type", "application/json")

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

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

	if c.verbose {
		var prettyJSON bytes.Buffer
		if err := json.Indent(&prettyJSON, body, "", "  "); err == nil {
			log.Printf("\n========== VERIFY RESPONSE ==========\nStatus: %d\nBody:\n%s\n=====================================\n", resp.StatusCode, prettyJSON.String())
		}
	}

	var apiResp VerifyResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return fmt.Errorf("unmarshaling response: %w (body: %s)", err, string(body))
	}

	if apiResp.Error != "" {
		return fmt.Errorf("API error: %s", apiResp.Error)
	}

	return nil
}

// GenerateSessionID generates a random alphanumeric session ID
func GenerateSessionID(length int) (string, error) {
	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	result := make([]byte, length)

	for i := range result {
		num, err := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
		if err != nil {
			return "", fmt.Errorf("generating random number: %w", err)
		}
		result[i] = charset[num.Int64()]
	}

	return string(result), nil
}
