package webagent

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log/slog"
	"net/http"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/runtimecontrol"
	"github.com/tidbcloud/AgentTeam/internal/service/agentmemory"
)

type createdAgentMemoryProvisionInput struct {
	store          AgentStore
	memory         CloudMemoryProvisioner
	runtime        Runtime
	logger         *slog.Logger
	userID         string
	workspaceID    string
	agentID        string
	machineID      string
	templateLocale string
}

func provisionCloudMemoryForCreatedAgent(ctx context.Context, input createdAgentMemoryProvisionInput) error {
	if input.memory == nil {
		return nil
	}
	err := input.memory.EnableAgentCloudMemory(ctx, agentmemory.EnableInput{
		UserID:                     input.userID,
		WorkspaceID:                input.workspaceID,
		AgentID:                    input.agentID,
		TemplateLocale:             input.templateLocale,
		ImportExistingLocalContent: true,
	})
	if err == nil {
		notifyRuntimeMemoryAccessChanged(ctx, input.runtime, input.logger, input.workspaceID, input.agentID, input.machineID)
		return nil
	}

	rollbackErr := error(nil)
	if input.store != nil {
		_, rollbackErr = input.store.DeleteAgentForWorkspace(ctx, input.userID, input.workspaceID, input.agentID)
	}
	if rollbackErr != nil {
		if input.logger != nil {
			input.logger.Error("rollback agent after cloud memory enable failure",
				slog.String("agent_id", input.agentID),
				slog.String("workspace_id", input.workspaceID),
				slog.Any("memory_error", err),
				slog.Any("rollback_error", rollbackErr))
		}
		return api.NewHTTPErrorWithCause(
			http.StatusInternalServerError,
			"cloud memory enable failed; agent rollback failed",
			errors.Join(err, rollbackErr),
		)
	}
	return cloudMemoryProvisionError(err)
}

func cloudMemoryProvisionError(err error) error {
	var dependencyErr agentmemory.DependencyError
	if errors.As(err, &dependencyErr) {
		status := dependencyErr.Status
		if status < 400 || status > 599 {
			status = http.StatusBadGateway
		}
		return api.NewHTTPErrorWithCause(status, cloudMemoryProvisionErrorMessage(dependencyErr, status), err)
	}
	return fmt.Errorf("enable cloud memory for created agent: %w", err)
}

func cloudMemoryProvisionErrorMessage(err agentmemory.DependencyError, status int) string {
	body := strings.TrimSpace(err.Message)
	if body != "" {
		var parsed struct {
			Message string `json:"message"`
			Error   string `json:"error"`
		}
		if json.Unmarshal([]byte(body), &parsed) == nil {
			if message := strings.TrimSpace(parsed.Message); message != "" {
				return message
			}
			if message := strings.TrimSpace(parsed.Error); message != "" {
				return message
			}
		}
		return body
	}
	if text := http.StatusText(status); text != "" {
		return text
	}
	return "cloud memory enable failed"
}

func notifyRuntimeMemoryAccessChanged(ctx context.Context, runtime Runtime, logger *slog.Logger, workspaceID string, agentID string, machineID string) {
	if runtime == nil || strings.TrimSpace(machineID) == "" {
		return
	}
	if logger != nil {
		logger.InfoContext(ctx, "notifying daemon of memory access change",
			slog.String("agent_id", agentID),
			slog.String("workspace_id", workspaceID),
			slog.String("machine_id", machineID))
	}
	if err := runtime.NotifyMemoryAccessChangedByAgent(ctx, workspaceID, agentID); err != nil && !errors.Is(err, runtimecontrol.ErrMachineOffline) {
		warn(logger, "notify memory access changed", slog.String("agent_id", agentID), slog.Any("error", err))
	}
}

func templateLocaleFromAcceptLanguage(raw string) string {
	trimmed := strings.TrimSpace(strings.ToLower(raw))
	if trimmed == "" {
		return "en"
	}
	for _, part := range strings.Split(trimmed, ",") {
		locale := strings.TrimSpace(strings.SplitN(part, ";", 2)[0])
		switch {
		case strings.HasPrefix(locale, "zh"):
			return "zh-CN"
		case strings.HasPrefix(locale, "ja"):
			return "ja"
		case strings.HasPrefix(locale, "en"):
			return "en"
		}
	}
	return "en"
}
