package http

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

	"github.com/rwadurian/mpc-system/pkg/logger"
	"github.com/rwadurian/mpc-system/services/session-coordinator/application/ports/output"
	"go.uber.org/zap"
)

// AccountServiceClient implements AccountServicePort using HTTP
type AccountServiceClient struct {
	baseURL    string
	httpClient *http.Client
}

// NewAccountServiceClient creates a new Account Service HTTP client
func NewAccountServiceClient(baseURL string) *AccountServiceClient {
	return &AccountServiceClient{
		baseURL: baseURL,
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// createAccountRequest represents the request body for account creation
type createAccountRequest struct {
	PublicKey       string              `json:"public_key"`
	KeygenSessionID string              `json:"keygen_session_id"`
	ThresholdN      int                 `json:"threshold_n"`
	ThresholdT      int                 `json:"threshold_t"`
	Shares          []shareInfoRequest  `json:"shares"`
}

type shareInfoRequest struct {
	PartyID    string `json:"party_id"`
	PartyIndex int    `json:"party_index"`
	ShareType  string `json:"share_type"`
}

// createAccountResponse represents the response from account creation
type createAccountResponse struct {
	AccountID string `json:"account_id"`
	Username  string `json:"username"`
	PublicKey string `json:"public_key"`
}

// CreateAccountFromKeygen creates an account record after successful keygen
func (c *AccountServiceClient) CreateAccountFromKeygen(
	ctx context.Context,
	input output.CreateAccountInput,
) (*output.CreateAccountOutput, error) {
	// Build request
	shares := make([]shareInfoRequest, len(input.Shares))
	for i, s := range input.Shares {
		shares[i] = shareInfoRequest{
			PartyID:    s.PartyID,
			PartyIndex: s.PartyIndex,
			ShareType:  s.ShareType,
		}
	}

	reqBody := createAccountRequest{
		PublicKey:       hex.EncodeToString(input.PublicKey),
		KeygenSessionID: input.KeygenSessionID,
		ThresholdN:      input.ThresholdN,
		ThresholdT:      input.ThresholdT,
		Shares:          shares,
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	// Make HTTP request
	url := fmt.Sprintf("%s/api/v1/accounts/from-keygen", c.baseURL)
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(jsonBody))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	logger.Info("Calling Account Service to create account from keygen",
		zap.String("url", url),
		zap.String("keygen_session_id", input.KeygenSessionID))

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to call account service: %w", err)
	}
	defer resp.Body.Close()

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

	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK {
		logger.Error("Account Service returned error",
			zap.Int("status_code", resp.StatusCode),
			zap.String("body", string(body)))
		return nil, fmt.Errorf("account service error: status=%d, body=%s", resp.StatusCode, string(body))
	}

	var result createAccountResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	logger.Info("Account created successfully from keygen",
		zap.String("account_id", result.AccountID),
		zap.String("keygen_session_id", input.KeygenSessionID))

	return &output.CreateAccountOutput{
		AccountID: result.AccountID,
		Success:   true,
	}, nil
}
