package webagent

import (
	"context"
	"errors"
	"net/http"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/api/daemon"
	"github.com/tidbcloud/AgentTeam/internal/api/dto"
)

type memorySyncAgentRoute struct {
	agentID   string
	machineID string
	canManage bool
}

type memorySyncAgentStore interface {
	AgentMemorySyncRouteForWorkspace(ctx context.Context, userID, workspaceID, agentID string) (memorySyncAgentRoute, bool, error)
}

type memorySyncRuntime interface {
	GetAgentMemorySyncStatus(ctx context.Context, machineID, agentID string) (daemon.AgentMemorySyncStatus, bool, error)
	RetryAgentMemorySync(ctx context.Context, machineID, agentID string) (bool, error)
}

// MemorySyncHandler exposes one agent's daemon-local Memory synchronization
// projection to an authorized browser client.
type MemorySyncHandler struct {
	agentStore memorySyncAgentStore
	runtime    memorySyncRuntime
}

// Status handles GET /api/v1/workspaces/:workspaceId/agents/:agentId/memory-sync.
// It reads local daemon state only and never reads the cloud repository.
func (handler *MemorySyncHandler) Status(r api.WorkspaceScopedRequest) (api.Response, error) {
	machineID, agentID, err := handler.agentRoute(r)
	if err != nil {
		return api.Response{}, err
	}
	status, available, err := handler.runtime.GetAgentMemorySyncStatus(r.Context(), machineID, agentID)
	if err != nil {
		return api.Response{}, memorySyncRuntimeError(err)
	}
	if !available {
		return api.Response{}, api.ErrNotFound
	}
	return api.OK(dto.AgentMemorySyncStatusResponse{
		State:   status.State,
		Reason:  status.Reason,
		RetryAt: status.RetryAt,
	}), nil
}

// Retry handles POST /api/v1/workspaces/:workspaceId/agents/:agentId/memory-sync/retry.
// It requests recovery through the daemon's existing serialized pipeline.
func (handler *MemorySyncHandler) Retry(r api.WorkspaceScopedRequest) (api.Response, error) {
	machineID, agentID, err := handler.agentRoute(r)
	if err != nil {
		return api.Response{}, err
	}
	requested, err := handler.runtime.RetryAgentMemorySync(r.Context(), machineID, agentID)
	if err != nil {
		return api.Response{}, memorySyncRuntimeError(err)
	}
	if !requested {
		return api.Response{}, api.ErrNotFound
	}
	return api.Response{
		Status: http.StatusAccepted,
		Body:   dto.AgentMemorySyncRetryResponse{RetryRequested: true},
	}, nil
}

func (handler *MemorySyncHandler) agentRoute(r api.WorkspaceScopedRequest) (string, string, error) {
	if handler.agentStore == nil || handler.runtime == nil {
		return "", "", api.NewHTTPError(http.StatusServiceUnavailable, "Memory sync status unavailable")
	}
	agentID := strings.TrimSpace(r.Gin.Param("agentId"))
	if agentID == "" {
		return "", "", api.NewHTTPError(http.StatusBadRequest, "agentId is required")
	}
	route, found, err := handler.agentStore.AgentMemorySyncRouteForWorkspace(r.Context(), r.UserID(), r.WorkspaceID(), agentID)
	if err != nil {
		return "", "", err
	}
	if !found {
		return "", "", api.ErrNotFound
	}
	if !route.canManage {
		return "", "", api.ErrForbidden
	}
	return route.machineID, route.agentID, nil
}

func memorySyncRuntimeError(err error) error {
	if errors.Is(err, daemon.ErrMemorySyncEndpointUnsupported) {
		return api.NewHTTPError(http.StatusNotImplemented, "Memory sync status requires a newer Desktop runtime")
	}
	return api.NewHTTPError(http.StatusServiceUnavailable, "Memory sync status unavailable")
}
