// ABOUTME: Cloudlet management APIs for EdgeXR Master Controller
// ABOUTME: Provides typed methods for creating, querying, and managing edge cloudlets

package v2

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	sdkhttp "edp.buildth.ing/DevFW-CICD/edge-connect-client/v2/sdk/internal/http"
)

// CreateCloudlet creates a new cloudlet in the specified region
// Maps to POST /auth/ctrl/CreateCloudlet
func (c *Client) CreateCloudlet(ctx context.Context, input *NewCloudletInput) error {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/CreateCloudlet"

	resp, err := transport.Call(ctx, "POST", url, input)
	if err != nil {
		return fmt.Errorf("CreateCloudlet failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode >= 400 {
		return c.handleErrorResponse(resp, "CreateCloudlet")
	}

	c.logf("CreateCloudlet: %s/%s created successfully",
		input.Cloudlet.Key.Organization, input.Cloudlet.Key.Name)

	return nil
}

// ShowCloudlet retrieves a single cloudlet by key and region
// Maps to POST /auth/ctrl/ShowCloudlet
func (c *Client) ShowCloudlet(ctx context.Context, cloudletKey CloudletKey, region string) (Cloudlet, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/ShowCloudlet"

	filter := CloudletFilter{
		Cloudlet: Cloudlet{Key: cloudletKey},
		Region:   region,
	}

	resp, err := transport.Call(ctx, "POST", url, filter)
	if err != nil {
		return Cloudlet{}, fmt.Errorf("ShowCloudlet failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode == http.StatusNotFound {
		return Cloudlet{}, fmt.Errorf("cloudlet %s/%s in region %s: %w",
			cloudletKey.Organization, cloudletKey.Name, region, ErrResourceNotFound)
	}

	if resp.StatusCode >= 400 {
		return Cloudlet{}, c.handleErrorResponse(resp, "ShowCloudlet")
	}

	// Parse streaming JSON response
	var cloudlets []Cloudlet
	if err := c.parseStreamingCloudletResponse(resp, &cloudlets); err != nil {
		return Cloudlet{}, fmt.Errorf("ShowCloudlet failed to parse response: %w", err)
	}

	if len(cloudlets) == 0 {
		return Cloudlet{}, fmt.Errorf("cloudlet %s/%s in region %s: %w",
			cloudletKey.Organization, cloudletKey.Name, region, ErrResourceNotFound)
	}

	return cloudlets[0], nil
}

// ShowCloudlets retrieves all cloudlets matching the filter criteria
// Maps to POST /auth/ctrl/ShowCloudlet
func (c *Client) ShowCloudlets(ctx context.Context, cloudletKey CloudletKey, region string) ([]Cloudlet, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/ShowCloudlet"

	filter := CloudletFilter{
		Cloudlet: Cloudlet{Key: cloudletKey},
		Region:   region,
	}

	resp, err := transport.Call(ctx, "POST", url, filter)
	if err != nil {
		return nil, fmt.Errorf("ShowCloudlets failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound {
		return nil, c.handleErrorResponse(resp, "ShowCloudlets")
	}

	var cloudlets []Cloudlet
	if resp.StatusCode == http.StatusNotFound {
		return cloudlets, nil // Return empty slice for not found
	}

	if err := c.parseStreamingCloudletResponse(resp, &cloudlets); err != nil {
		return nil, fmt.Errorf("ShowCloudlets failed to parse response: %w", err)
	}

	c.logf("ShowCloudlets: found %d cloudlets matching criteria", len(cloudlets))
	return cloudlets, nil
}

// DeleteCloudlet removes a cloudlet from the specified region
// Maps to POST /auth/ctrl/DeleteCloudlet
func (c *Client) DeleteCloudlet(ctx context.Context, cloudletKey CloudletKey, region string) error {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/DeleteCloudlet"

	filter := CloudletFilter{
		Cloudlet: Cloudlet{Key: cloudletKey},
		Region:   region,
	}

	resp, err := transport.Call(ctx, "POST", url, filter)
	if err != nil {
		return fmt.Errorf("DeleteCloudlet failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	// 404 is acceptable for delete operations (already deleted)
	if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound {
		return c.handleErrorResponse(resp, "DeleteCloudlet")
	}

	c.logf("DeleteCloudlet: %s/%s deleted successfully",
		cloudletKey.Organization, cloudletKey.Name)

	return nil
}

// GetCloudletManifest retrieves the deployment manifest for a cloudlet
// Maps to POST /auth/ctrl/GetCloudletManifest
func (c *Client) GetCloudletManifest(ctx context.Context, cloudletKey CloudletKey, region string) (*CloudletManifest, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/GetCloudletManifest"

	filter := CloudletFilter{
		Cloudlet: Cloudlet{Key: cloudletKey},
		Region:   region,
	}

	resp, err := transport.Call(ctx, "POST", url, filter)
	if err != nil {
		return nil, fmt.Errorf("GetCloudletManifest failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode == http.StatusNotFound {
		return nil, fmt.Errorf("cloudlet manifest %s/%s in region %s: %w",
			cloudletKey.Organization, cloudletKey.Name, region, ErrResourceNotFound)
	}

	if resp.StatusCode >= 400 {
		return nil, c.handleErrorResponse(resp, "GetCloudletManifest")
	}

	// Parse the response as CloudletManifest
	var manifest CloudletManifest
	if err := c.parseDirectJSONResponse(resp, &manifest); err != nil {
		return nil, fmt.Errorf("GetCloudletManifest failed to parse response: %w", err)
	}

	c.logf("GetCloudletManifest: retrieved manifest for %s/%s",
		cloudletKey.Organization, cloudletKey.Name)

	return &manifest, nil
}

// GetCloudletResourceUsage retrieves resource usage information for a cloudlet
// Maps to POST /auth/ctrl/GetCloudletResourceUsage
func (c *Client) GetCloudletResourceUsage(ctx context.Context, cloudletKey CloudletKey, region string) (*CloudletResourceUsage, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/GetCloudletResourceUsage"

	filter := CloudletFilter{
		Cloudlet: Cloudlet{Key: cloudletKey},
		Region:   region,
	}

	resp, err := transport.Call(ctx, "POST", url, filter)
	if err != nil {
		return nil, fmt.Errorf("GetCloudletResourceUsage failed: %w", err)
	}
	defer func() {
		_ = resp.Body.Close()
	}()

	if resp.StatusCode == http.StatusNotFound {
		return nil, fmt.Errorf("cloudlet resource usage %s/%s in region %s: %w",
			cloudletKey.Organization, cloudletKey.Name, region, ErrResourceNotFound)
	}

	if resp.StatusCode >= 400 {
		return nil, c.handleErrorResponse(resp, "GetCloudletResourceUsage")
	}

	// Parse the response as CloudletResourceUsage
	var usage CloudletResourceUsage
	if err := c.parseDirectJSONResponse(resp, &usage); err != nil {
		return nil, fmt.Errorf("GetCloudletResourceUsage failed to parse response: %w", err)
	}

	c.logf("GetCloudletResourceUsage: retrieved usage for %s/%s",
		cloudletKey.Organization, cloudletKey.Name)

	return &usage, nil
}

// parseStreamingCloudletResponse parses the EdgeXR streaming JSON response format for cloudlets
func (c *Client) parseStreamingCloudletResponse(resp *http.Response, result interface{}) error {
	var responses []Response[Cloudlet]

	parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
		var response Response[Cloudlet]
		if err := json.Unmarshal(line, &response); err != nil {
			return err
		}
		responses = append(responses, response)
		return nil
	})

	if parseErr != nil {
		return parseErr
	}

	// Extract data from responses
	var cloudlets []Cloudlet
	var messages []string

	for _, response := range responses {
		if response.HasData() {
			cloudlets = append(cloudlets, response.Data)
		}
		if response.IsMessage() {
			messages = append(messages, response.Data.GetMessage())
		}
	}

	// If we have error messages, return them
	if len(messages) > 0 {
		return &APIError{
			StatusCode: resp.StatusCode,
			Messages:   messages,
		}
	}

	// Set result based on type
	switch v := result.(type) {
	case *[]Cloudlet:
		*v = cloudlets
	default:
		return fmt.Errorf("unsupported result type: %T", result)
	}

	return nil
}

// parseDirectJSONResponse parses a direct JSON response (not streaming)
func (c *Client) parseDirectJSONResponse(resp *http.Response, result interface{}) error {
	decoder := json.NewDecoder(resp.Body)
	if err := decoder.Decode(result); err != nil {
		return fmt.Errorf("failed to decode JSON response: %w", err)
	}
	return nil
}
