// Package enroll implements the OramaOS node enrollment command.
//
// Flow:
//  1. Operator fetches registration code from the OramaOS node (port 9999)
//  2. Operator provides code + invite token to Gateway
//  3. Gateway validates, generates cluster config, pushes to node
//  4. Node configures WireGuard, encrypts data partition, starts services
package enroll

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

// Handle processes the enroll command.
func Handle(args []string) {
	flags, err := ParseFlags(args)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	// Step 1: Fetch registration code from the OramaOS node
	fmt.Printf("Fetching registration code from %s:9999...\n", flags.NodeIP)

	var code string
	if flags.Code != "" {
		// Code provided directly — skip fetch
		code = flags.Code
	} else {
		fetchedCode, err := fetchRegistrationCode(flags.NodeIP)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: could not reach OramaOS node: %v\n", err)
			fmt.Fprintf(os.Stderr, "Make sure the node is booted and port 9999 is reachable.\n")
			os.Exit(1)
		}
		code = fetchedCode
	}

	fmt.Printf("Registration code: %s\n", code)

	// Step 2: Send enrollment request to the Gateway
	fmt.Printf("Sending enrollment to Gateway at %s...\n", flags.GatewayURL)

	if err := enrollWithGateway(flags.GatewayURL, flags.Token, code, flags.NodeIP); err != nil {
		fmt.Fprintf(os.Stderr, "Error: enrollment failed: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("Node %s enrolled successfully.\n", flags.NodeIP)
	fmt.Printf("The node is now configuring WireGuard and encrypting its data partition.\n")
	fmt.Printf("This may take a few minutes. Check status with: orama node status --env %s\n", flags.Env)
}

// fetchRegistrationCode retrieves the one-time registration code from the OramaOS node.
func fetchRegistrationCode(nodeIP string) (string, error) {
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Get(fmt.Sprintf("http://%s:9999/", nodeIP))
	if err != nil {
		return "", fmt.Errorf("GET failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusGone {
		return "", fmt.Errorf("registration code already served (node may be partially enrolled)")
	}
	if resp.StatusCode != http.StatusOK {
		return "", fmt.Errorf("unexpected status %d", resp.StatusCode)
	}

	var result struct {
		Code    string `json:"code"`
		Expires string `json:"expires"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return "", fmt.Errorf("invalid response: %w", err)
	}

	return result.Code, nil
}

// enrollWithGateway sends the enrollment request to the Gateway, which validates
// the code and token, then pushes cluster configuration to the OramaOS node.
func enrollWithGateway(gatewayURL, token, code, nodeIP string) error {
	body, _ := json.Marshal(map[string]string{
		"code":    code,
		"token":   token,
		"node_ip": nodeIP,
	})

	req, err := http.NewRequest("POST", gatewayURL+"/v1/node/enroll", bytes.NewReader(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusUnauthorized {
		return fmt.Errorf("invalid or expired invite token")
	}
	if resp.StatusCode == http.StatusBadRequest {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("bad request: %s", string(respBody))
	}
	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("gateway returned %d: %s", resp.StatusCode, string(respBody))
	}

	return nil
}
