package lookup

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

	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
)

type machineAuthenticator interface {
	AuthenticateMachine(context.Context, string) (agent.MachineIdentity, error)
}

type agentAuthenticator interface {
	AuthenticateAgent(context.Context, string, string) (agent.AuthenticatedAgent, error)
}

type appRunnerAuthenticator interface {
	ResolveAppRuntimeWorkspaceByBearerToken(context.Context, string) (string, bool, error)
}

// LegacyRuntimeResolver authenticates old unscoped runtime URLs and returns
// the workspace already bound to the machine, Agent, or App credential.
type LegacyRuntimeResolver struct {
	machines machineAuthenticator
	agents   agentAuthenticator
	apps     appRunnerAuthenticator
}

func NewLegacyRuntimeResolver(machines machineAuthenticator, agents agentAuthenticator, apps appRunnerAuthenticator) *LegacyRuntimeResolver {
	return &LegacyRuntimeResolver{machines: machines, agents: agents, apps: apps}
}

func (resolver *LegacyRuntimeResolver) ResolveWorkspaceRouteIdentity(ctx context.Context, _ string, path, _ string, rawQuery, authorization, _, _ string) (string, bool, error) {
	if resolver == nil {
		return "", false, nil
	}
	switch {
	case pathMatchesLegacyPrefix(path, "/daemon"):
		if resolver.machines == nil {
			return "", false, nil
		}
		key := bearerAuthorizationToken(authorization)
		if key == "" {
			query, err := url.ParseQuery(rawQuery)
			if err != nil {
				return "", false, nil
			}
			key = strings.TrimSpace(query.Get("key"))
		}
		if key == "" {
			return "", false, storeerr.ErrUnauthorized
		}
		identity, err := resolver.machines.AuthenticateMachine(ctx, key)
		return legacyWorkspaceResult(identity.WorkspaceID, err)
	case pathMatchesLegacyPrefix(path, "/internal/agent"):
		if resolver.agents == nil {
			return "", false, nil
		}
		agentID, ok := legacyAgentID(path)
		if !ok {
			return "", false, nil
		}
		token := bearerAuthorizationToken(authorization)
		if token == "" {
			return "", false, storeerr.ErrUnauthorized
		}
		identity, err := resolver.agents.AuthenticateAgent(ctx, agentID, token)
		return legacyWorkspaceResult(identity.WorkspaceID, err)
	case pathMatchesLegacyPrefix(path, "/internal/app/runtime"):
		if resolver.apps == nil {
			return "", false, nil
		}
		token := bearerAuthorizationToken(authorization)
		if token == "" {
			return "", false, storeerr.ErrUnauthorized
		}
		workspaceID, resolved, err := resolver.apps.ResolveAppRuntimeWorkspaceByBearerToken(ctx, token)
		if err != nil {
			return "", false, err
		}
		if !resolved {
			return "", false, storeerr.ErrUnauthorized
		}
		return legacyWorkspaceResult(workspaceID, nil)
	default:
		return "", false, nil
	}
}

func legacyWorkspaceResult(workspaceID string, err error) (string, bool, error) {
	if err != nil {
		if errors.Is(err, storeerr.ErrUnauthorized) || errors.Is(err, storeerr.ErrNotFound) {
			return "", false, storeerr.ErrUnauthorized
		}
		return "", false, err
	}
	workspaceID = strings.TrimSpace(workspaceID)
	return workspaceID, workspaceID != "", nil
}

func pathMatchesLegacyPrefix(path, prefix string) bool {
	path = strings.TrimSpace(path)
	return path == prefix || strings.HasPrefix(path, prefix+"/")
}

func legacyAgentID(path string) (string, bool) {
	parts := strings.Split(strings.Trim(path, "/"), "/")
	if len(parts) < 3 || parts[0] != "internal" || parts[1] != "agent" {
		return "", false
	}
	agentID := strings.TrimSpace(parts[2])
	return agentID, agentID != ""
}
