package workbench

import (
	"encoding/json"
	"errors"
	"net/http"
	"strconv"
	"strings"

	"aigateway.local/core/internal/identity"
	"aigateway.local/core/internal/platform/apiresponse"
)

type AdminHTTPHandler struct {
	service       *Service
	tools         *ToolService
	notifications *NotificationService
	identity      *identity.Service
	mux           *http.ServeMux
}

func NewAdminHTTPHandler(service *Service, tools *ToolService, notifications *NotificationService, identityService *identity.Service) *AdminHTTPHandler {
	h := &AdminHTTPHandler{service: service, tools: tools, notifications: notifications, identity: identityService, mux: http.NewServeMux()}
	h.mux.HandleFunc("GET /api/v1/admin/prompt-categories", h.listPromptCategories)
	h.mux.HandleFunc("POST /api/v1/admin/prompt-categories", h.createPromptCategory)
	h.mux.HandleFunc("DELETE /api/v1/admin/prompt-categories/{id}", h.deletePromptCategory)
	h.mux.HandleFunc("GET /api/v1/admin/prompts", h.listPrompts)
	h.mux.HandleFunc("POST /api/v1/admin/prompts", h.createPrompt)
	h.mux.HandleFunc("GET /api/v1/admin/prompts/{id}", h.getPrompt)
	h.mux.HandleFunc("PUT /api/v1/admin/prompts/{id}", h.updatePrompt)
	h.mux.HandleFunc("DELETE /api/v1/admin/prompts/{id}", h.deletePrompt)
	h.mux.HandleFunc("GET /api/v1/admin/prompts/{id}/versions", h.listPromptVersions)
	h.mux.HandleFunc("POST /api/v1/admin/prompts/{id}/versions", h.addPromptVersion)
	h.mux.HandleFunc("POST /api/v1/admin/prompts/{id}/versions/{version}/activate", h.activatePromptVersion)
	h.mux.HandleFunc("POST /api/v1/admin/prompts/{id}/render", h.previewPrompt)
	h.mux.HandleFunc("GET /api/v1/admin/knowledge-bases", h.listKnowledgeBases)
	h.mux.HandleFunc("POST /api/v1/admin/knowledge-bases", h.createKnowledgeBase)
	h.mux.HandleFunc("PUT /api/v1/admin/knowledge-bases/{id}", h.updateKnowledgeBase)
	h.mux.HandleFunc("DELETE /api/v1/admin/knowledge-bases/{id}", h.deleteKnowledgeBase)
	h.mux.HandleFunc("GET /api/v1/admin/knowledge-bases/{id}/documents", h.listDocuments)
	h.mux.HandleFunc("POST /api/v1/admin/knowledge-bases/{id}/documents", h.addDocument)
	h.mux.HandleFunc("DELETE /api/v1/admin/knowledge-bases/{id}/documents/{document_id}", h.deleteDocument)
	h.mux.HandleFunc("POST /api/v1/admin/knowledge-bases/{id}/documents/{document_id}/reprocess", h.reprocessDocument)
	h.mux.HandleFunc("POST /api/v1/admin/knowledge-bases/{id}/search", h.searchKnowledge)
	h.mux.HandleFunc("GET /api/v1/admin/tools", h.listTools)
	h.mux.HandleFunc("POST /api/v1/admin/tools", h.createTool)
	h.mux.HandleFunc("PUT /api/v1/admin/tools/{id}", h.updateTool)
	h.mux.HandleFunc("DELETE /api/v1/admin/tools/{id}", h.deleteTool)
	h.mux.HandleFunc("POST /api/v1/admin/tools/{id}/test", h.testTool)
	h.mux.HandleFunc("GET /api/v1/admin/applications", h.listApplications)
	h.mux.HandleFunc("POST /api/v1/admin/applications", h.createApplication)
	h.mux.HandleFunc("GET /api/v1/admin/applications/catalog", h.applicationCatalog)
	h.mux.HandleFunc("GET /api/v1/admin/applications/asset-dependencies", h.applicationDependencies)
	h.mux.HandleFunc("GET /api/v1/admin/applications/{id}", h.getApplication)
	h.mux.HandleFunc("PUT /api/v1/admin/applications/{id}", h.updateApplication)
	h.mux.HandleFunc("DELETE /api/v1/admin/applications/{id}", h.deleteApplication)
	h.mux.HandleFunc("POST /api/v1/admin/applications/{id}/publish", h.publishApplication)
	h.mux.HandleFunc("POST /api/v1/admin/applications/{id}/rollback/{version}", h.rollbackApplication)
	h.mux.HandleFunc("GET /api/v1/admin/applications/{id}/versions", h.listApplicationVersions)
	h.mux.HandleFunc("GET /api/v1/admin/applications/{id}/runs", h.listApplicationRuns)
	h.mux.HandleFunc("GET /api/v1/admin/notification-channels", h.listChannels)
	h.mux.HandleFunc("POST /api/v1/admin/notification-channels", h.createChannel)
	h.mux.HandleFunc("PUT /api/v1/admin/notification-channels/{id}", h.updateChannel)
	h.mux.HandleFunc("DELETE /api/v1/admin/notification-channels/{id}", h.deleteChannel)
	h.mux.HandleFunc("GET /api/v1/admin/notification-deliveries", h.listDeliveries)
	h.mux.HandleFunc("POST /api/v1/admin/notification-deliveries/{id}/retry", h.retryDelivery)
	return h
}
func (h *AdminHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.mux.ServeHTTP(w, r) }
func (h *AdminHTTPHandler) require(w http.ResponseWriter, r *http.Request, permission string) (identity.Account, bool) {
	account, err := h.identity.Authenticate(r.Context(), identity.KindAdmin, r.Header.Get("Authorization"))
	if err != nil {
		apiresponse.Error(w, 401, "登录状态无效")
		return account, false
	}
	if !identity.HasPermission(account, permission) {
		apiresponse.Error(w, 403, "缺少 AI 资产管理权限")
		return account, false
	}
	return account, true
}
func decodeAsset(w http.ResponseWriter, r *http.Request, target any) bool {
	decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 3<<20))
	decoder.DisallowUnknownFields()
	if err := decoder.Decode(target); err != nil {
		apiresponse.Error(w, 400, "请求格式无效")
		return false
	}
	return true
}
func assetError(w http.ResponseWriter, err error) {
	switch {
	case errors.Is(err, ErrNotFound):
		apiresponse.Error(w, 404, "资源不存在")
	case errors.Is(err, ErrConflict):
		apiresponse.Error(w, 409, "资源仍被已发布或草稿应用引用")
	default:
		apiresponse.Error(w, 400, err.Error())
	}
}

func (h *AdminHTTPHandler) listPromptCategories(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionPromptRead); !ok {
		return
	}
	items, err := h.service.ListPromptCategories(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) createPromptCategory(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	var input struct {
		Name        string `json:"name"`
		Description string `json:"description"`
	}
	if !decodeAsset(w, r, &input) {
		return
	}
	item, err := h.service.CreatePromptCategory(r.Context(), input.Name, input.Description, a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deletePromptCategory(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	if err := h.service.DeletePromptCategory(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}

type promptPayload struct {
	Name          string     `json:"name"`
	Description   string     `json:"description"`
	CategoryID    *string    `json:"category_id"`
	Tags          []string   `json:"tags"`
	DepartmentIDs []string   `json:"department_ids"`
	Enabled       bool       `json:"enabled"`
	Content       string     `json:"content"`
	Variables     []Variable `json:"variables"`
	ChangeNote    string     `json:"change_note"`
}

func promptInput(p promptPayload) PromptInput {
	return PromptInput{Name: p.Name, Description: p.Description, CategoryID: p.CategoryID, Tags: p.Tags, DepartmentIDs: p.DepartmentIDs, Enabled: p.Enabled, Content: p.Content, Variables: p.Variables, ChangeNote: p.ChangeNote}
}
func (h *AdminHTTPHandler) listPrompts(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionPromptRead); !ok {
		return
	}
	items, err := h.service.ListPrompts(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) getPrompt(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionPromptRead); !ok {
		return
	}
	item, err := h.service.GetPrompt(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) createPrompt(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	var p promptPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.service.CreatePrompt(r.Context(), promptInput(p), a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) updatePrompt(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	var p promptPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.service.UpdatePrompt(r.Context(), r.PathValue("id"), promptInput(p), a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deletePrompt(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	if err := h.service.DeletePrompt(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) listPromptVersions(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionPromptRead); !ok {
		return
	}
	items, err := h.service.ListPromptVersions(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) addPromptVersion(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	var p struct {
		Content    string     `json:"content"`
		Variables  []Variable `json:"variables"`
		ChangeNote string     `json:"change_note"`
		Activate   bool       `json:"activate"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.service.AddPromptVersion(r.Context(), r.PathValue("id"), p.Content, p.Variables, p.ChangeNote, a.ID, p.Activate)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) activatePromptVersion(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionPromptManage)
	if !ok {
		return
	}
	version, err := strconv.Atoi(r.PathValue("version"))
	if err != nil {
		apiresponse.Error(w, 400, "版本号无效")
		return
	}
	if err = h.service.ActivatePromptVersion(r.Context(), r.PathValue("id"), version, a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"activated": true})
}
func (h *AdminHTTPHandler) previewPrompt(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionPromptRead); !ok {
		return
	}
	var p struct {
		Variables map[string]any `json:"variables"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	prompt, err := h.service.GetPrompt(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	if prompt.Current == nil {
		apiresponse.Error(w, 409, "Prompt 没有生效版本")
		return
	}
	rendered, err := RenderPrompt(prompt.Current.Content, prompt.Current.Variables, p.Variables)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]any{"rendered": rendered, "version": prompt.Current.Version})
}

func (h *AdminHTTPHandler) listKnowledgeBases(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionKnowledgeRead); !ok {
		return
	}
	items, err := h.service.ListKnowledgeBases(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) createKnowledgeBase(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	var k KnowledgeBase
	if !decodeAsset(w, r, &k) {
		return
	}
	item, err := h.service.SaveKnowledgeBase(r.Context(), k, a.ID, true)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) updateKnowledgeBase(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	var k KnowledgeBase
	if !decodeAsset(w, r, &k) {
		return
	}
	k.ID = r.PathValue("id")
	item, err := h.service.SaveKnowledgeBase(r.Context(), k, a.ID, false)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deleteKnowledgeBase(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	if err := h.service.DeleteKnowledgeBase(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) listDocuments(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionKnowledgeRead); !ok {
		return
	}
	items, err := h.service.ListKnowledgeDocuments(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) addDocument(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	var p struct {
		Title      string `json:"title"`
		SourceType string `json:"source_type"`
		SourceURI  string `json:"source_uri"`
		Content    string `json:"content"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.service.AddKnowledgeDocument(r.Context(), r.PathValue("id"), p.Title, p.SourceType, p.SourceURI, p.Content, a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deleteDocument(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	if err := h.service.DeleteKnowledgeDocument(r.Context(), r.PathValue("id"), r.PathValue("document_id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) reprocessDocument(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionKnowledgeManage)
	if !ok {
		return
	}
	item, err := h.service.ReprocessKnowledgeDocument(r.Context(), r.PathValue("id"), r.PathValue("document_id"), a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) searchKnowledge(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionKnowledgeRead); !ok {
		return
	}
	var p struct {
		Query string `json:"query"`
		TopK  int    `json:"top_k"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	items, err := NewPostgreSQLRetriever(h.service).Search(r.Context(), r.PathValue("id"), p.Query, p.TopK)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}

type toolPayload struct {
	Code           string             `json:"code"`
	Name           string             `json:"name"`
	Description    string             `json:"description"`
	EndpointURL    string             `json:"endpoint_url"`
	HTTPMethod     string             `json:"http_method"`
	Headers        *map[string]string `json:"headers"`
	InputSchema    json.RawMessage    `json:"input_schema"`
	TimeoutSeconds int                `json:"timeout_seconds"`
	DepartmentIDs  []string           `json:"department_ids"`
	Enabled        bool               `json:"enabled"`
}

func toolInput(p toolPayload, create bool) ToolInput {
	var headers map[string]string
	if p.Headers != nil {
		headers = *p.Headers
	} else if create {
		headers = map[string]string{}
	}
	return ToolInput{Code: p.Code, Name: p.Name, Description: p.Description, EndpointURL: p.EndpointURL, HTTPMethod: p.HTTPMethod, Headers: headers, InputSchema: p.InputSchema, TimeoutSeconds: p.TimeoutSeconds, DepartmentIDs: p.DepartmentIDs, Enabled: p.Enabled}
}
func (h *AdminHTTPHandler) listTools(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionToolRead); !ok {
		return
	}
	items, err := h.tools.List(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) createTool(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionToolManage)
	if !ok {
		return
	}
	var p toolPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.tools.Save(r.Context(), "", toolInput(p, true), a.ID, true)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) updateTool(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionToolManage)
	if !ok {
		return
	}
	var p toolPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.tools.Save(r.Context(), r.PathValue("id"), toolInput(p, false), a.ID, false)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deleteTool(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionToolManage)
	if !ok {
		return
	}
	if err := h.tools.Delete(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) testTool(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionToolManage); !ok {
		return
	}
	var p struct {
		Input map[string]any `json:"input"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	tool, err := h.tools.Get(r.Context(), r.PathValue("id"))
	if err == nil {
		result, executeErr := h.tools.Execute(r.Context(), tool, p.Input, "", "admin-test")
		err = executeErr
		if err == nil {
			apiresponse.OK(w, result)
			return
		}
	}
	assetError(w, err)
}

func (h *AdminHTTPHandler) listApplications(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	items, err := h.service.ListApplications(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) getApplication(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	item, err := h.service.GetApplication(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	versions, err := h.service.ListApplicationVersions(r.Context(), item.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]any{"application": item, "versions": versions})
}
func (h *AdminHTTPHandler) applicationCatalog(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	prompts, err := h.service.ListPrompts(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	knowledge, err := h.service.ListKnowledgeBases(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	tools, err := h.tools.List(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]any{"prompts": prompts, "knowledge_bases": knowledge, "tools": tools})
}
func (h *AdminHTTPHandler) applicationDependencies(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	assetType := strings.TrimSpace(r.URL.Query().Get("asset_type"))
	assetID := strings.TrimSpace(r.URL.Query().Get("asset_id"))
	if assetID == "" || (assetType != "prompt" && assetType != "knowledge" && assetType != "tool") {
		apiresponse.Error(w, 400, "asset_type 或 asset_id 无效")
		return
	}
	items, err := h.service.ApplicationDependencies(r.Context(), assetType, assetID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) createApplication(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionApplicationManage)
	if !ok {
		return
	}
	var app Application
	if !decodeAsset(w, r, &app) {
		return
	}
	item, err := h.service.SaveApplication(r.Context(), app, a.ID, true)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) updateApplication(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionApplicationManage)
	if !ok {
		return
	}
	var app Application
	if !decodeAsset(w, r, &app) {
		return
	}
	app.ID = r.PathValue("id")
	item, err := h.service.SaveApplication(r.Context(), app, a.ID, false)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deleteApplication(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionApplicationManage)
	if !ok {
		return
	}
	if err := h.service.DeleteApplication(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) publishApplication(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionApplicationManage)
	if !ok {
		return
	}
	var p struct {
		ChangeNote string `json:"change_note"`
	}
	if !decodeAsset(w, r, &p) {
		return
	}
	app, err := h.service.PublishApplication(r.Context(), r.PathValue("id"), p.ChangeNote, a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, app)
}
func (h *AdminHTTPHandler) rollbackApplication(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionApplicationManage)
	if !ok {
		return
	}
	version, err := strconv.Atoi(r.PathValue("version"))
	if err != nil || version < 1 {
		apiresponse.Error(w, 400, "版本号无效")
		return
	}
	var input struct {
		ChangeNote string `json:"change_note"`
	}
	if !decodeAsset(w, r, &input) {
		return
	}
	item, err := h.service.RollbackApplication(r.Context(), r.PathValue("id"), version, input.ChangeNote, a.ID)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) listApplicationVersions(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	items, err := h.service.ListApplicationVersions(r.Context(), r.PathValue("id"))
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) listApplicationRuns(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionApplicationRead); !ok {
		return
	}
	items, err := h.service.ListApplicationRuns(r.Context(), r.PathValue("id"), 100)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}

type channelPayload struct {
	Name          string   `json:"name"`
	WebhookURL    string   `json:"webhook_url"`
	SigningSecret *string  `json:"signing_secret"`
	EventPatterns []string `json:"event_patterns"`
	Enabled       bool     `json:"enabled"`
}

func (h *AdminHTTPHandler) listChannels(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionNotificationRead); !ok {
		return
	}
	items, err := h.notifications.ListChannels(r.Context())
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) createChannel(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionNotificationManage)
	if !ok {
		return
	}
	var p channelPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.notifications.SaveChannel(r.Context(), "", NotificationInput{Name: p.Name, WebhookURL: p.WebhookURL, SigningSecret: p.SigningSecret, EventPatterns: p.EventPatterns, Enabled: p.Enabled}, a.ID, true)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) updateChannel(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionNotificationManage)
	if !ok {
		return
	}
	var p channelPayload
	if !decodeAsset(w, r, &p) {
		return
	}
	item, err := h.notifications.SaveChannel(r.Context(), r.PathValue("id"), NotificationInput{Name: p.Name, WebhookURL: p.WebhookURL, SigningSecret: p.SigningSecret, EventPatterns: p.EventPatterns, Enabled: p.Enabled}, a.ID, false)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, item)
}
func (h *AdminHTTPHandler) deleteChannel(w http.ResponseWriter, r *http.Request) {
	a, ok := h.require(w, r, identity.PermissionNotificationManage)
	if !ok {
		return
	}
	if err := h.notifications.DeleteChannel(r.Context(), r.PathValue("id"), a.ID); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"deleted": true})
}
func (h *AdminHTTPHandler) listDeliveries(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionNotificationRead); !ok {
		return
	}
	items, err := h.notifications.ListDeliveries(r.Context(), 100)
	if err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, items)
}
func (h *AdminHTTPHandler) retryDelivery(w http.ResponseWriter, r *http.Request) {
	if _, ok := h.require(w, r, identity.PermissionNotificationManage); !ok {
		return
	}
	if err := h.notifications.RetryDelivery(r.Context(), r.PathValue("id")); err != nil {
		assetError(w, err)
		return
	}
	apiresponse.OK(w, map[string]bool{"retried": true})
}

var _ = strings.TrimSpace
