// ABOUTME: Application instance lifecycle management APIs for EdgeXR Master Controller
// ABOUTME: Provides typed methods for creating, querying, and deleting application instances

package edgeconnect

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

	"edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/core/domain"
	sdkhttp "edp.buildth.ing/DevFW-CICD/edge-connect-client/internal/adapters/internal/http"
)

// CreateAppInstance creates a new application instance in the specified region
// Maps to POST /auth/ctrl/CreateAppInst
func (c *Client) CreateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
	transport := c.getTransport()
	apiAppInst := ToAPIAppInstance(appInst)
	input := &NewAppInstanceInput{
		Region:  region,
		AppInst: *apiAppInst,
	}
	url := c.BaseURL + "/api/v1/auth/ctrl/CreateAppInst"

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

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

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

	return nil
}

// ShowAppInstance retrieves a single application instance by key and region
// Maps to POST /auth/ctrl/ShowAppInst
func (c *Client) ShowAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) (*domain.AppInstance, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/ShowAppInst"

	apiAppInstKey := toAPIAppInstanceKey(appInstKey)
	filter := AppInstanceFilter{
		AppInstance: AppInstance{Key: *apiAppInstKey},
		Region:      region,
	}

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

	if resp.StatusCode == http.StatusNotFound {
		return nil, domain.NewInstanceError(domain.ErrResourceNotFound, "ShowAppInstance", appInstKey, region, 
			"app instance not found")
	}

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

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

	if len(appInstances) == 0 {
		return nil, domain.NewInstanceError(domain.ErrResourceNotFound, "ShowAppInstance", appInstKey, region, 
			"app instance not found")
	}

	domainAppInst := toDomainAppInstance(&appInstances[0])
	return &domainAppInst, nil
}

// ShowAppInstances retrieves all application instances matching the filter criteria
// Maps to POST /auth/ctrl/ShowAppInst
func (c *Client) ShowAppInstances(ctx context.Context, region string, appInstKey domain.AppInstanceKey) ([]domain.AppInstance, error) {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/ShowAppInst"

	apiAppInstKey := toAPIAppInstanceKey(appInstKey)
	filter := AppInstanceFilter{
		AppInstance: AppInstance{Key: *apiAppInstKey},
		Region:      region,
	}

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

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

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

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

	c.logf("ShowAppInstances: found %d app instances matching criteria", len(appInstances))
	
	domainAppInsts := make([]domain.AppInstance, len(appInstances))
	for i := range appInstances {
		domainAppInsts[i] = toDomainAppInstance(&appInstances[i])
	}

	return domainAppInsts, nil
}

// UpdateAppInstance updates an application instance and then refreshes it
// Maps to POST /auth/ctrl/UpdateAppInst
func (c *Client) UpdateAppInstance(ctx context.Context, region string, appInst *domain.AppInstance) error {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/UpdateAppInst"

	apiAppInst := ToAPIAppInstance(appInst)
	input := &UpdateAppInstanceInput{
		Region:  region,
		AppInst: *apiAppInst,
	}
	resp, err := transport.Call(ctx, "POST", url, input)
	if err != nil {
		return fmt.Errorf("UpdateAppInstance failed: %w", err)
	}
	defer resp.Body.Close()

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

	c.logf("UpdateAppInstance: %s/%s updated successfully",
		input.AppInst.Key.Organization, input.AppInst.Key.Name)

	return nil
}

// RefreshAppInstance refreshes an application instance's state
// Maps to POST /auth/ctrl/RefreshAppInst
func (c *Client) RefreshAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/RefreshAppInst"

	apiAppInstKey := toAPIAppInstanceKey(appInstKey)
	filter := AppInstanceFilter{
		AppInstance: AppInstance{Key: *apiAppInstKey},
		Region:      region,
	}

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

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

	c.logf("RefreshAppInstance: %s/%s refreshed successfully",
		appInstKey.Organization, appInstKey.Name)

	return nil
}

// DeleteAppInstance removes an application instance from the specified region
// Maps to POST /auth/ctrl/DeleteAppInst
func (c *Client) DeleteAppInstance(ctx context.Context, region string, appInstKey domain.AppInstanceKey) error {
	transport := c.getTransport()
	url := c.BaseURL + "/api/v1/auth/ctrl/DeleteAppInst"

	apiAppInstKey := toAPIAppInstanceKey(appInstKey)
	filter := AppInstanceFilter{
		AppInstance: AppInstance{Key: *apiAppInstKey},
		Region:      region,
	}

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

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

	c.logf("DeleteAppInstance: %s/%s deleted successfully",
		appInstKey.Organization, appInstKey.Name)

	return nil
}

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

	parseErr := sdkhttp.ParseJSONLines(resp.Body, func(line []byte) error {
		var response Response[AppInstance]
		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 appInstances []AppInstance
	var messages []string

	for _, response := range responses {
		if response.HasData() {
			appInstances = append(appInstances, 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 *[]AppInstance:
		*v = appInstances
	default:
		return fmt.Errorf("unsupported result type: %T", result)
	}

	return nil
}

func ToAPIAppInstance(appInst *domain.AppInstance) *AppInstance {
	return &AppInstance{
		Key:        *toAPIAppInstanceKey(appInst.Key),
		AppKey:     *toAPIAppKey(appInst.AppKey),
		Flavor:     toAPIFlavor(appInst.Flavor),
		State:      appInst.State,
		PowerState: appInst.PowerState,
		Fields:     appInst.Fields,
	}
}

func toDomainAppInstance(appInst *AppInstance) domain.AppInstance {
	return domain.AppInstance{
		Key:        toDomainAppInstanceKey(appInst.Key),
		AppKey:     toDomainAppKey(appInst.AppKey),
		Flavor:     toDomainFlavor(appInst.Flavor),
		State:      appInst.State,
		PowerState: appInst.PowerState,
		Fields:     appInst.Fields,
	}
}

func toAPIAppInstanceKey(key domain.AppInstanceKey) *AppInstanceKey {
	return &AppInstanceKey{
		Organization: key.Organization,
		Name:         key.Name,
		CloudletKey:  toAPICloudletKey(key.CloudletKey),
	}
}

func toDomainAppInstanceKey(key AppInstanceKey) domain.AppInstanceKey {
	return domain.AppInstanceKey{
		Organization: key.Organization,
		Name:         key.Name,
		CloudletKey:  toDomainCloudletKey(key.CloudletKey),
	}
}

func toAPICloudletKey(key domain.CloudletKey) CloudletKey {
	return CloudletKey{
		Organization: key.Organization,
		Name:         key.Name,
	}
}

func toDomainCloudletKey(key CloudletKey) domain.CloudletKey {
	return domain.CloudletKey{
		Organization: key.Organization,
		Name:         key.Name,
	}
}