package plan

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

	"github.com/tidbcloud/AgentTeam/internal/workflow/planmanage"
)

const workflowPlanCreatorRequiredCode = "workflow_plan_creator_required"

type handlerBase struct {
	service Service
	planMutationAuthorizer
}

type planReader interface {
	Get(context.Context, string, string) (planmanage.Plan, error)
}

type planMutationAuthorizer struct {
	plans                  planReader
	authoringAuthority     PlanAuthoringAuthority
	directPublishGuard     DirectPublishGuard
	planManagementResolver PlanManagementResolver
}

type CollectionHandler struct{ handlerBase }
type DetailHandler struct {
	handlerBase
	agentEdit AgentEditService
}
type DraftHandler struct{ handlerBase }
type VersionHandler struct{ handlerBase }
type ValidationHandler struct{ handlerBase }

type routeHandlers struct {
	collection *CollectionHandler
	detail     *DetailHandler
	draft      *DraftHandler
	versions   *VersionHandler
	validation *ValidationHandler
}

func newRouteHandlers(service Service, guards ...DirectPublishGuard) routeHandlers {
	var guard DirectPublishGuard
	var resolver PlanManagementResolver
	if len(guards) != 0 {
		guard = guards[0]
		resolver, _ = guard.(PlanManagementResolver)
	}
	authoringAuthority, _ := service.(PlanAuthoringAuthority)
	base := handlerBase{
		service: service,
		planMutationAuthorizer: planMutationAuthorizer{
			plans:                  service,
			authoringAuthority:     authoringAuthority,
			directPublishGuard:     guard,
			planManagementResolver: resolver,
		},
	}
	return routeHandlers{
		collection: &CollectionHandler{handlerBase: base},
		detail:     &DetailHandler{handlerBase: base},
		draft:      &DraftHandler{handlerBase: base},
		versions:   &VersionHandler{handlerBase: base},
		validation: &ValidationHandler{handlerBase: base},
	}
}

func (authorizer planMutationAuthorizer) requirePlanCreator(
	ctx context.Context,
	workspaceID string,
	planID string,
	memberID string,
) error {
	allowed, err := authorizer.planAuthoringAllowed(ctx, workspaceID, planID, memberID)
	if err != nil {
		return err
	}
	if !allowed {
		return publicError(http.StatusForbidden, workflowPlanCreatorRequiredCode, "Only the Workflow creator may modify this Workflow")
	}
	return nil
}

func (authorizer planMutationAuthorizer) planAuthoringAllowed(
	ctx context.Context,
	workspaceID string,
	planID string,
	memberID string,
) (bool, error) {
	if authorizer.authoringAuthority != nil {
		return authorizer.authoringAuthority.CanAuthor(ctx, workspaceID, planID, memberID)
	}
	plan, err := authorizer.plans.Get(ctx, workspaceID, planID)
	if err != nil {
		return false, err
	}
	return planCreatorMatches(plan, memberID), nil
}

// requireWorkspaceAuthoringAllowed preserves Team Workflow ownership while
// enforcing durable authoring authority for ordinary workspace Plans.
func (authorizer planMutationAuthorizer) requireWorkspaceAuthoringAllowed(
	ctx context.Context,
	workspaceID string,
	planID string,
	memberID string,
) error {
	allowed, err := authorizer.planAuthoringAllowed(ctx, workspaceID, planID, memberID)
	if err != nil {
		return err
	}
	if allowed {
		return nil
	}
	if authorizer.planManagementResolver != nil {
		_, _, _, linked, err := authorizer.planManagementResolver.ResolvePlanManagement(
			ctx, workspaceID, planID,
		)
		if err != nil {
			return err
		}
		if linked {
			return nil
		}
	}
	return publicError(http.StatusForbidden, workflowPlanCreatorRequiredCode, "Only the Workflow creator may modify this Workflow")
}

func (authorizer planMutationAuthorizer) requireDirectPlanMutationAllowed(
	ctx context.Context,
	workspaceID string,
	planID string,
	memberID string,
) error {
	if authorizer.directPublishGuard != nil {
		if err := authorizer.directPublishGuard.RequireUnlinkedPlan(
			ctx, workspaceID, planID,
		); err != nil {
			return err
		}
	}
	return authorizer.requirePlanCreator(ctx, workspaceID, planID, memberID)
}

func planCreatorMatches(plan planmanage.Plan, memberID string) bool {
	creatorMemberID := strings.TrimSpace(plan.CreatedBy.MemberID)
	return creatorMemberID != "" && creatorMemberID == strings.TrimSpace(memberID)
}
