package quota

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

	"github.com/pkg/errors"
)

// QuotaClient checks quotas via the Sankofa API
type QuotaClient struct {
	apiURL     string
	apiToken   string
	httpClient *http.Client
}

// NewQuotaClient creates a new quota client
func NewQuotaClient(apiURL, apiToken string) *QuotaClient {
	return &QuotaClient{
		apiURL:   apiURL,
		apiToken: apiToken,
		httpClient: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// ResourceRequest represents a resource provisioning request
type ResourceRequest struct {
	Compute *ComputeRequest `json:"compute,omitempty"`
	Storage *StorageRequest `json:"storage,omitempty"`
	Network *NetworkRequest `json:"network,omitempty"`
}

// ComputeRequest represents compute resource requirements
type ComputeRequest struct {
	VCPU     *int `json:"vcpu,omitempty"`
	Memory   *int `json:"memory,omitempty"` // in GB
	Instances *int `json:"instances,omitempty"`
}

// StorageRequest represents storage resource requirements
type StorageRequest struct {
	Size *int `json:"size,omitempty"` // in GB
}

// NetworkRequest represents network resource requirements
type NetworkRequest struct {
	Bandwidth *int `json:"bandwidth,omitempty"` // in Mbps
}

// QuotaCheckResponse represents the response from quota check
type QuotaCheckResponse struct {
	Allowed  bool     `json:"allowed"`
	Exceeded []string `json:"exceeded,omitempty"`
	Current  struct {
		Compute struct {
			Instances int `json:"instances"`
			VCPU      int `json:"vcpu"`
			Memory    int `json:"memory"`
		} `json:"compute"`
		Storage struct {
			Total      int `json:"total"`
			PerInstance int `json:"perInstance"`
		} `json:"storage"`
		Network struct {
			Bandwidth int `json:"bandwidth"`
			Egress    int `json:"egress"`
		} `json:"network"`
	} `json:"current"`
	Limits struct {
		Compute struct {
			Instances *int `json:"instances,omitempty"`
			VCPU      *int `json:"vcpu,omitempty"`
			Memory    *int `json:"memory,omitempty"`
		} `json:"compute"`
		Storage struct {
			Total       *int `json:"total,omitempty"`
			PerInstance *int `json:"perInstance,omitempty"`
		} `json:"storage"`
		Network struct {
			Bandwidth *int `json:"bandwidth,omitempty"`
			Egress    *int `json:"egress,omitempty"`
		} `json:"network"`
	} `json:"limits"`
}

// CheckQuota checks if a resource request would exceed tenant quotas
func (c *QuotaClient) CheckQuota(ctx context.Context, tenantID string, request ResourceRequest) (*QuotaCheckResponse, error) {
	// Build GraphQL query with proper variable substitution
	resourceRequestJSON := c.buildResourceRequestJSON(request)
	query := fmt.Sprintf(`mutation { checkQuota(tenantId: "%s", resourceRequest: %s) { allowed exceeded current { compute { instances vcpu memory } storage { total perInstance } network { bandwidth egress } } limits { compute { instances vcpu memory } storage { total perInstance } network { bandwidth egress } } } }`, tenantID, resourceRequestJSON)

	// Make GraphQL request
	reqBody := map[string]interface{}{
		"query": query,
	}

	jsonBody, err := json.Marshal(reqBody)
	if err != nil {
		return nil, errors.Wrap(err, "failed to marshal request")
	}

	req, err := http.NewRequestWithContext(ctx, "POST", c.apiURL+"/graphql", bytes.NewBuffer(jsonBody))
	if err != nil {
		return nil, errors.Wrap(err, "failed to create request")
	}

	req.Header.Set("Content-Type", "application/json")
	if c.apiToken != "" {
		req.Header.Set("Authorization", "Bearer "+c.apiToken)
	}

	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, errors.Wrap(err, "failed to execute request")
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("quota check failed with status %d", resp.StatusCode)
	}

	var graphqlResp struct {
		Data struct {
			CheckQuota QuotaCheckResponse `json:"checkQuota"`
		} `json:"data"`
		Errors []struct {
			Message string `json:"message"`
		} `json:"errors,omitempty"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&graphqlResp); err != nil {
		return nil, errors.Wrap(err, "failed to decode response")
	}

	if len(graphqlResp.Errors) > 0 {
		return nil, fmt.Errorf("GraphQL errors: %v", graphqlResp.Errors)
	}

	return &graphqlResp.Data.CheckQuota, nil
}

// EnforceQuota checks quota and returns an error if exceeded
func (c *QuotaClient) EnforceQuota(ctx context.Context, tenantID string, request ResourceRequest) error {
	check, err := c.CheckQuota(ctx, tenantID, request)
	if err != nil {
		return errors.Wrap(err, "failed to check quota")
	}

	if !check.Allowed {
		return fmt.Errorf("quota exceeded: %v", check.Exceeded)
	}

	return nil
}

// buildResourceRequestJSON converts ResourceRequest to GraphQL input format
func (c *QuotaClient) buildResourceRequestJSON(request ResourceRequest) string {
	var parts []string

	if request.Compute != nil {
		computeParts := []string{}
		if request.Compute.VCPU != nil {
			computeParts = append(computeParts, fmt.Sprintf("vcpu: %d", *request.Compute.VCPU))
		}
		if request.Compute.Memory != nil {
			computeParts = append(computeParts, fmt.Sprintf("memory: %d", *request.Compute.Memory))
		}
		if request.Compute.Instances != nil {
			computeParts = append(computeParts, fmt.Sprintf("instances: %d", *request.Compute.Instances))
		}
		if len(computeParts) > 0 {
			parts = append(parts, fmt.Sprintf("compute: { %s }", strings.Join(computeParts, ", ")))
		}
	}

	if request.Storage != nil && request.Storage.Size != nil {
		parts = append(parts, fmt.Sprintf("storage: { size: %d }", *request.Storage.Size))
	}

	if request.Network != nil && request.Network.Bandwidth != nil {
		parts = append(parts, fmt.Sprintf("network: { bandwidth: %d }", *request.Network.Bandwidth))
	}

	if len(parts) == 0 {
		return "{}"
	}

	return "{ " + strings.Join(parts, ", ") + " }"
}

