package main

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

	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
)

type Claims struct {
	SessionID string `json:"session_id"`
	PartyID   string `json:"party_id"`
	TokenType string `json:"token_type"`
	jwt.RegisteredClaims
}

func generateAccessToken(secretKey, userID, username string) (string, error) {
	now := time.Now()
	claims := Claims{
		PartyID:   username,
		TokenType: "access",
		RegisteredClaims: jwt.RegisteredClaims{
			ID:        uuid.New().String(),
			Issuer:    "mpc-system",
			Subject:   userID,
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)),
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(secretKey))
}

func makeRequest(method, url string, body interface{}, token, apiKey string) (int, []byte, error) {
	var reqBody io.Reader
	if body != nil {
		jsonData, err := json.Marshal(body)
		if err != nil {
			return 0, nil, err
		}
		reqBody = bytes.NewBuffer(jsonData)
	}

	req, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return 0, nil, err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-API-Key", apiKey)
	req.Header.Set("Authorization", "Bearer "+token)

	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return 0, nil, err
	}
	defer resp.Body.Close()

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return 0, nil, err
	}

	return resp.StatusCode, respBody, nil
}

func main() {
	// Get JWT secret from environment or use test value
	jwtSecret := os.Getenv("JWT_SECRET_KEY")
	if jwtSecret == "" {
		jwtSecret = "change_this_jwt_secret_key_to_random_value_min_32_chars"
	}

	// Generate access token
	token, err := generateAccessToken(jwtSecret, "admin", "admin")
	if err != nil {
		fmt.Printf("Failed to generate token: %v\n", err)
		os.Exit(1)
	}

	// Get API key from environment or use test value
	apiKey := os.Getenv("MPC_API_KEY")
	if apiKey == "" {
		apiKey = "test-api-key"
	}

	// Use the wallet created from keygen test
	username := "wallet-f13135f7"
	baseURL := "http://localhost:4000/api/v1/accounts/by-username/" + username + "/signing-config"

	fmt.Println("===========================================")
	fmt.Println("Testing Signing Parties Configuration APIs")
	fmt.Println("===========================================")
	fmt.Printf("Username: %s\n\n", username)

	// Test 1: GET - Query current signing parties configuration
	fmt.Println("--------------------------------------------")
	fmt.Println("Test 1: GET signing-config (Query)")
	fmt.Println("--------------------------------------------")
	status, body, err := makeRequest("GET", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Extract active parties from the response for later tests
	var activeParties []string
	var result map[string]interface{}
	json.Unmarshal(body, &result)
	if parties, ok := result["active_parties"].([]interface{}); ok {
		for _, p := range parties {
			if s, ok := p.(string); ok {
				activeParties = append(activeParties, s)
			}
		}
	}
	fmt.Printf("\nActive parties found: %v\n", activeParties)

	if len(activeParties) < 2 {
		fmt.Println("\nError: Need at least 2 active parties for testing")
		os.Exit(1)
	}

	// Test 2: POST - Set signing parties (select first 2 parties)
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 2: POST signing-config (Set)")
	fmt.Println("--------------------------------------------")
	signingParties := activeParties[:2] // Select first 2 parties
	setData := map[string]interface{}{
		"party_ids": signingParties,
	}
	fmt.Printf("Setting signing parties to: %v\n", signingParties)
	status, body, err = makeRequest("POST", baseURL, setData, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 3: GET - Query again to verify it was set
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 3: GET signing-config (Verify Set)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("GET", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 4: POST again - Should fail (already configured)
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 4: POST signing-config (Should Fail - Already Set)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("POST", baseURL, setData, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d (expected 409 Conflict)\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 5: PUT - Update signing parties (use different combination if possible)
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 5: PUT signing-config (Update)")
	fmt.Println("--------------------------------------------")
	var updateParties []string
	if len(activeParties) >= 3 {
		// If we have 3 parties, use parties 2 and 3 (different combination)
		updateParties = []string{activeParties[1], activeParties[2]}
	} else {
		// Otherwise, reverse the order
		updateParties = []string{signingParties[1], signingParties[0]}
	}
	updateData := map[string]interface{}{
		"party_ids": updateParties,
	}
	fmt.Printf("Updating signing parties to: %v\n", updateParties)
	status, body, err = makeRequest("PUT", baseURL, updateData, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 6: GET - Query to verify update
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 6: GET signing-config (Verify Update)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("GET", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 7: DELETE - Clear signing parties configuration
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 7: DELETE signing-config (Clear)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("DELETE", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 8: GET - Query to verify cleared
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 8: GET signing-config (Verify Cleared)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("GET", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 9: DELETE again - Should fail (not configured)
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 9: DELETE signing-config (Should Fail - Not Configured)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("DELETE", baseURL, nil, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d (expected 404 Not Found)\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	// Test 10: PUT when not configured - Should fail
	fmt.Println("\n--------------------------------------------")
	fmt.Println("Test 10: PUT signing-config (Should Fail - Not Configured)")
	fmt.Println("--------------------------------------------")
	status, body, err = makeRequest("PUT", baseURL, setData, token, apiKey)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
	} else {
		fmt.Printf("Status: %d (expected 404 Not Found)\n", status)
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		prettyJSON, _ := json.MarshalIndent(result, "", "  ")
		fmt.Printf("Response:\n%s\n", prettyJSON)
	}

	fmt.Println("\n===========================================")
	fmt.Println("All tests completed!")
	fmt.Println("===========================================")
}
