package plan

import (
	"errors"
	"net/http"

	"github.com/tidbcloud/AgentTeam/internal/api"
	"github.com/tidbcloud/AgentTeam/internal/store/storeerr"
	"github.com/tidbcloud/AgentTeam/internal/workflow/planmanage"
	"github.com/tidbcloud/AgentTeam/internal/workflow/publicerr"
)

// publicError constructs a canonical API-layer domain error that the shared
// classifier projects into the canonical public Workflow error contract. Status
// is authoritative; the caller-supplied problem is bounded before projection.
func publicError(status int, code string, problem string) error {
	return publicerr.WorkflowError{
		Kind: storeerr.ErrBadRequest, Status: status, Code: code, Problem: problem,
	}
}

func classifyError(err error) (api.ErrorResponse, bool) {
	// The Plan Agent wake lifecycle error is a canonical API-layer domain
	// failure with a stable code, bounded problem, and stage. Route it through
	// the shared public classifier so every canonical contract field (stage,
	// path, hint, retryability, diagnostics) survives the
	// classifier→ErrorResponse→writer path instead of an ad-hoc envelope.
	if errors.Is(err, planmanage.ErrAgentWakeActive) {
		return workflowErrorResponse(classifyPlanWakeActiveError()), true
	}
	classified, ok := publicerr.Classify(err, publicerr.Options{
		Stage:       publicerr.StagePlan,
		DefaultCode: "plan_request_failed",
		StatusForCode: func(code string, status int) int {
			if code == string(planmanage.ErrorCodeRevisionConflict) || code == string(planmanage.ErrorCodeDraftConflict) {
				return http.StatusPreconditionFailed
			}
			return status
		},
		ProtectedSelectors: map[string]string{
			string(planmanage.ErrorCodeNotFound): string(planmanage.ErrorCodeNotFound),
		},
		ProtectedNotFoundCode: string(planmanage.ErrorCodeNotFound),
	})
	if !ok {
		return api.ErrorResponse{}, false
	}
	return workflowErrorResponse(classified), true
}

// classifyPlanWakeActiveError projects the Plan Agent wake-active lifecycle
// error through the canonical public Workflow classifier with the owning Plan
// stage, a stable code, a bounded problem, and no raw cause. The wrapped
// storeerr.ErrConflict kind yields the 409 status; every canonical contract
// field (stage/path/hint/retryable/diagnostics) is carried on the response.
func classifyPlanWakeActiveError() publicerr.Classified {
	classified, _ := publicerr.Classify(
		publicerr.WorkflowError{
			Kind: storeerr.ErrConflict, Code: "workflow_agent_wake_active", Problem: "Workflow Agent wake is active",
		},
		publicerr.Options{Stage: publicerr.StagePlan, DefaultCode: "plan_request_failed"},
	)
	return classified
}

// workflowErrorResponse maps a canonical public Workflow classification into the
// shared API error envelope, carrying every canonical contract field.
func workflowErrorResponse(classified publicerr.Classified) api.ErrorResponse {
	response := api.ErrorResponse{
		Status: classified.Status, Message: classified.Error.Problem,
		Code: classified.Error.Code, Details: classified.Details,
		Stage: string(classified.Error.Stage), Path: classified.Error.Path,
		Hint: classified.Error.Hint, Retryable: classified.Error.Retryable,
		Diagnostics: classified.Error.Diagnostics,
	}
	if classified.Error.Truncation != nil {
		response.Truncation = classified.Error.Truncation
	}
	return response
}
