package identity

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

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

type HTTPHandler struct {
	service *Service
	mux     *http.ServeMux
}

type loginRequest struct {
	UserName string `json:"userName"`
	Username string `json:"username"`
	Account  string `json:"account"`
	Password string `json:"password"`
}

type totpLoginRequest struct {
	TempToken  string `json:"temp_token"`
	Code       string `json:"code"`
	BackupCode string `json:"backup_code"`
}

type passwordRequest struct {
	Password string `json:"password"`
}

type factorRequest struct {
	Password   string `json:"password"`
	Code       string `json:"code"`
	BackupCode string `json:"backup_code"`
}

func NewHTTPHandler(service *Service) *HTTPHandler {
	handler := &HTTPHandler{service: service, mux: http.NewServeMux()}
	handler.mux.HandleFunc("POST /api/v1/admin/login", handler.login(KindAdmin))
	handler.registerTOTP(KindAdmin, "/api/v1/admin")
	handler.mux.HandleFunc("GET /api/v1/admin/whoami", handler.whoami(KindAdmin))
	handler.mux.HandleFunc("POST /api/v1/admin/password", handler.changePassword(KindAdmin))
	handler.mux.HandleFunc("POST /api/v1/admin/logout", handler.logout)
	handler.mux.HandleFunc("GET /api/v1/admin/menus", handler.menus(KindAdmin))
	handler.mux.HandleFunc("POST /api/v1/portal/login", handler.login(KindPortal))
	handler.registerTOTP(KindPortal, "/api/v1/portal")
	handler.mux.HandleFunc("GET /api/v1/portal/me", handler.whoami(KindPortal))
	handler.mux.HandleFunc("POST /api/v1/portal/logout", handler.logout)
	handler.mux.HandleFunc("GET /api/v1/portal/menus", handler.menus(KindPortal))
	handler.registerOIDC()
	return handler
}

func (h *HTTPHandler) changePassword(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		var input struct {
			OldPassword string `json:"old_password"`
			NewPassword string `json:"new_password"`
		}
		if !decodeJSON(writer, request, &input) {
			apiresponse.Error(writer, http.StatusBadRequest, "请求格式无效")
			return
		}
		if err := h.service.ChangePassword(request.Context(), account, input.OldPassword, input.NewPassword); err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]bool{"changed": true})
	}
}

func (h *HTTPHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
	h.mux.ServeHTTP(writer, request)
}

func (h *HTTPHandler) login(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		// 防爆破：按 IP 的滑动窗口限流，超限返回 429（与账号锁定叠加）。
		if !h.service.AllowLogin(request.Context(), ClientIP(request)) {
			apiresponse.Error(writer, http.StatusTooManyRequests, "登录尝试过于频繁，请稍后再试")
			return
		}
		var input loginRequest
		decoder := json.NewDecoder(http.MaxBytesReader(writer, request.Body, 1<<20))
		decoder.DisallowUnknownFields()
		if err := decoder.Decode(&input); err != nil {
			apiresponse.Error(writer, http.StatusBadRequest, "请求格式无效")
			return
		}
		login := strings.TrimSpace(input.UserName)
		if login == "" {
			login = strings.TrimSpace(input.Username)
		}
		if login == "" {
			login = strings.TrimSpace(input.Account)
		}
		if login == "" || len(login) > 128 || len(input.Password) < 1 || len(input.Password) > 1024 {
			apiresponse.Error(writer, http.StatusBadRequest, "账号或口令格式无效")
			return
		}
		result, err := h.service.Login(request.Context(), kind, login, input.Password)
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]any{
			"token": result.Token, "refreshToken": "", "require_totp": result.RequireTOTP,
			"temp_token": result.TempToken,
		})
	}
}

func (h *HTTPHandler) registerTOTP(kind Kind, prefix string) {
	h.mux.HandleFunc("POST "+prefix+"/login/totp", h.completeTOTPLogin(kind))
	h.mux.HandleFunc("GET "+prefix+"/totp/status", h.totpStatus(kind))
	h.mux.HandleFunc("POST "+prefix+"/totp/setup", h.setupTOTP(kind))
	h.mux.HandleFunc("POST "+prefix+"/totp/confirm", h.confirmTOTP(kind))
	h.mux.HandleFunc("POST "+prefix+"/totp/disable", h.disableTOTP(kind))
	h.mux.HandleFunc("POST "+prefix+"/totp/backup-codes/regenerate", h.regenerateBackupCodes(kind))
}

func (h *HTTPHandler) completeTOTPLogin(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		// 防爆破：TOTP 完成端点同样按 IP 限流。
		if !h.service.AllowLogin(request.Context(), ClientIP(request)) {
			apiresponse.Error(writer, http.StatusTooManyRequests, "登录尝试过于频繁，请稍后再试")
			return
		}
		var input totpLoginRequest
		if !decodeJSON(writer, request, &input) || strings.TrimSpace(input.TempToken) == "" || (strings.TrimSpace(input.Code) == "" && strings.TrimSpace(input.BackupCode) == "") {
			apiresponse.Error(writer, http.StatusBadRequest, "请输入动态验证码或备用码")
			return
		}
		result, err := h.service.CompleteTOTPLogin(request.Context(), kind, input.TempToken, input.Code, input.BackupCode)
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]any{"token": result.Token, "refreshToken": "", "require_totp": false})
	}
}

func (h *HTTPHandler) totpStatus(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		apiresponse.OK(writer, map[string]bool{"enabled": account.TOTPEnabled, "setup_pending": !account.TOTPEnabled && len(account.EncryptedTOTPSecret) > 0})
	}
}

func (h *HTTPHandler) setupTOTP(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		var input passwordRequest
		if !decodeJSON(writer, request, &input) || input.Password == "" {
			apiresponse.Error(writer, http.StatusBadRequest, "请输入当前口令")
			return
		}
		result, err := h.service.SetupTOTP(request.Context(), account, input.Password)
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]string{"secret": result.Secret, "provisioning_uri": result.ProvisioningURI})
	}
}

func (h *HTTPHandler) confirmTOTP(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		var input factorRequest
		if !decodeJSON(writer, request, &input) || strings.TrimSpace(input.Code) == "" {
			apiresponse.Error(writer, http.StatusBadRequest, "请输入动态验证码")
			return
		}
		codes, err := h.service.ConfirmTOTP(request.Context(), account, input.Code)
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]any{"enabled": true, "backup_codes": codes})
	}
}

func (h *HTTPHandler) disableTOTP(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		var input factorRequest
		if !decodeJSON(writer, request, &input) || input.Password == "" {
			apiresponse.Error(writer, http.StatusBadRequest, "当前口令和验证因子不能为空")
			return
		}
		if err := h.service.DisableTOTP(request.Context(), account, input.Password, input.Code, input.BackupCode); err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]bool{"enabled": false})
	}
}

func (h *HTTPHandler) regenerateBackupCodes(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, ok := h.requireAccount(writer, request, kind)
		if !ok {
			return
		}
		var input factorRequest
		if !decodeJSON(writer, request, &input) || input.Password == "" {
			apiresponse.Error(writer, http.StatusBadRequest, "当前口令和验证因子不能为空")
			return
		}
		codes, err := h.service.RegenerateBackupCodes(request.Context(), account, input.Password, input.Code, input.BackupCode)
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		apiresponse.OK(writer, map[string]any{"backup_codes": codes})
	}
}

func (h *HTTPHandler) requireAccount(writer http.ResponseWriter, request *http.Request, kind Kind) (Account, bool) {
	account, err := h.service.Authenticate(request.Context(), kind, request.Header.Get("Authorization"))
	if err != nil {
		h.writeIdentityError(writer, err)
		return Account{}, false
	}
	return account, true
}

func decodeJSON(writer http.ResponseWriter, request *http.Request, target any) bool {
	decoder := json.NewDecoder(http.MaxBytesReader(writer, request.Body, 1<<20))
	decoder.DisallowUnknownFields()
	return decoder.Decode(target) == nil
}

func (h *HTTPHandler) whoami(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, err := h.service.Authenticate(request.Context(), kind, request.Header.Get("Authorization"))
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		roles := []string{"R_USER"}
		if kind == KindAdmin {
			roles = []string{"R_ADMIN"}
			if account.Role == "superadmin" {
				roles = []string{"R_SUPER"}
			}
		}
		permissions := EffectivePermissions(account)
		apiresponse.OK(writer, map[string]any{
			"userId": account.ID, "userName": account.Login,
			"displayName": account.DisplayName, "email": "",
			"roles": roles, "buttons": permissions, "permissions": permissions, "role": account.Role,
		})
	}
}

func (h *HTTPHandler) logout(writer http.ResponseWriter, request *http.Request) {
	if err := h.service.Logout(request.Context(), request.Header.Get("Authorization")); err != nil && !errors.Is(err, ErrInvalidSession) {
		h.writeIdentityError(writer, err)
		return
	}
	apiresponse.OK(writer, map[string]bool{"ok": true})
}

func (h *HTTPHandler) menus(kind Kind) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		account, err := h.service.Authenticate(request.Context(), kind, request.Header.Get("Authorization"))
		if err != nil {
			h.writeIdentityError(writer, err)
			return
		}
		if kind == KindAdmin {
			apiresponse.OK(writer, adminMenus(account))
			return
		}
		apiresponse.OK(writer, portalMenus())
	}
}

func (h *HTTPHandler) writeIdentityError(writer http.ResponseWriter, err error) {
	var locked LockedError
	switch {
	case errors.As(err, &locked):
		minutes := int(time.Until(locked.Until).Minutes()) + 1
		apiresponse.Error(writer, http.StatusTooManyRequests, "账号已锁定，请在 "+(time.Duration(minutes)*time.Minute).String()+" 后重试")
	case errors.Is(err, ErrInvalidCredentials):
		apiresponse.Error(writer, http.StatusUnauthorized, "账号或口令错误")
	case errors.Is(err, ErrInvalidSession), errors.Is(err, ErrNotFound):
		apiresponse.Error(writer, http.StatusUnauthorized, "登录状态无效或已过期")
	case errors.Is(err, ErrAccountDisabled):
		apiresponse.Error(writer, http.StatusForbidden, "账号已被停用")
	case errors.Is(err, ErrInvalidTOTP):
		apiresponse.Error(writer, http.StatusUnauthorized, "动态验证码无效、已使用或备用码无效")
	case errors.Is(err, ErrTOTPAlreadyEnabled):
		apiresponse.Error(writer, http.StatusConflict, "两步验证已经启用")
	case errors.Is(err, ErrTOTPNotEnabled), errors.Is(err, ErrTOTPSetupRequired):
		apiresponse.Error(writer, http.StatusConflict, "两步验证尚未完成配置")
	case errors.Is(err, cryptox.ErrKeyUnavailable):
		apiresponse.Error(writer, http.StatusServiceUnavailable, "两步验证加密密钥不可用")
	case errors.Is(err, ErrUnavailable):
		apiresponse.Error(writer, http.StatusServiceUnavailable, "身份服务暂不可用")
	default:
		apiresponse.Error(writer, http.StatusInternalServerError, "身份服务处理失败")
	}
}

func adminMenus(account Account) []map[string]any {
	// 运行概览：首页仪表盘（叶子菜单，避免出现「运行概览>运行概览」同级冗余）。
	menus := []map[string]any{
		{"name": "Dashboard", "path": "/dashboard/console", "component": "/dashboard/console", "meta": map[string]any{"title": "运行概览", "icon": "ri:pie-chart-line", "fixedTab": true}},
	}

	// 网关接入：上游供应商、路由与凭据。
	gatewayChildren := make([]map[string]any, 0, 4)
	if HasPermission(account, PermissionProviderRead) || HasPermission(account, PermissionProviderManage) {
		gatewayChildren = append(gatewayChildren, map[string]any{"name": "Providers", "path": "providers", "component": "/gateway/providers", "meta": map[string]any{"title": "模型供应商"}})
		gatewayChildren = append(gatewayChildren, map[string]any{"name": "ModelRoutes", "path": "model-routes", "component": "/gateway/model-routes", "meta": map[string]any{"title": "模型路由"}})
	}
	if HasPermission(account, PermissionAPIKeyRead) || HasPermission(account, PermissionAPIKeyManage) {
		gatewayChildren = append(gatewayChildren, map[string]any{"name": "APIKeys", "path": "api-keys", "component": "/gateway/api-keys", "meta": map[string]any{"title": "API Key"}})
	}
	if HasPermission(account, PermissionPricingRead) || HasPermission(account, PermissionPricingManage) {
		gatewayChildren = append(gatewayChildren, map[string]any{"name": "ModelPrices", "path": "model-prices", "component": "/gateway/model-prices", "meta": map[string]any{"title": "模型价格"}})
	}
	if len(gatewayChildren) > 0 {
		menus = append(menus, map[string]any{"name": "Gateway", "path": "/gateway", "component": "/index/index", "meta": map[string]any{"title": "网关接入", "icon": "ri:router-line"}, "children": gatewayChildren})
	}

	// 安全与审计：审计用量、内容策略与模型治理。
	securityChildren := make([]map[string]any, 0, 3)
	if HasPermission(account, PermissionAuditRead) || HasPermission(account, PermissionUsageRead) {
		securityChildren = append(securityChildren, map[string]any{"name": "AuditUsage", "path": "audit-usage", "component": "/gateway/audit-usage", "meta": map[string]any{"title": "审计与用量"}})
	}
	if HasPermission(account, PermissionContentPolicyRead) || HasPermission(account, PermissionContentPolicyManage) {
		securityChildren = append(securityChildren, map[string]any{"name": "ContentPolicies", "path": "content-policies", "component": "/gateway/content-policies", "meta": map[string]any{"title": "内容策略"}})
	}
	if HasPermission(account, PermissionKnowledgeRead) || HasPermission(account, PermissionKnowledgeManage) {
		securityChildren = append(securityChildren, map[string]any{"name": "Governance", "path": "governance", "component": "/gateway/governance", "meta": map[string]any{"title": "模型治理"}})
	}
	if len(securityChildren) > 0 {
		menus = append(menus, map[string]any{"name": "Security", "path": "/security", "component": "/index/index", "meta": map[string]any{"title": "安全与审计", "icon": "ri:shield-check-line"}, "children": securityChildren})
	}

	// AI 资产：Prompt、知识库、工具与 AI 应用。
	assetsChildren := make([]map[string]any, 0, 4)
	if HasPermission(account, PermissionPromptRead) || HasPermission(account, PermissionPromptManage) {
		assetsChildren = append(assetsChildren, map[string]any{"name": "Prompts", "path": "prompts", "component": "/gateway/prompts", "meta": map[string]any{"title": "Prompt 资产"}})
	}
	if HasPermission(account, PermissionKnowledgeRead) || HasPermission(account, PermissionKnowledgeManage) {
		assetsChildren = append(assetsChildren, map[string]any{"name": "Knowledge", "path": "knowledge", "component": "/gateway/knowledge", "meta": map[string]any{"title": "知识库"}})
	}
	if HasPermission(account, PermissionToolRead) || HasPermission(account, PermissionToolManage) {
		assetsChildren = append(assetsChildren, map[string]any{"name": "Tools", "path": "tools", "component": "/gateway/tools", "meta": map[string]any{"title": "工具中心"}})
	}
	if HasPermission(account, PermissionApplicationRead) || HasPermission(account, PermissionApplicationManage) {
		assetsChildren = append(assetsChildren, map[string]any{"name": "Applications", "path": "applications", "component": "/gateway/applications", "meta": map[string]any{"title": "AI 应用"}})
	}
	if HasPermission(account, PermissionFileRead) || HasPermission(account, PermissionFileManage) {
		assetsChildren = append(assetsChildren, map[string]any{"name": "Files", "path": "files", "component": "/gateway/files", "meta": map[string]any{"title": "文件管理"}})
	}
	if len(assetsChildren) > 0 {
		menus = append(menus, map[string]any{"name": "Assets", "path": "/assets", "component": "/index/index", "meta": map[string]any{"title": "AI 资产", "icon": "ri:box-3-line"}, "children": assetsChildren})
	}

	// 资源市场：MCP 服务器、Skills 与数字员工（旗舰版资源市场）。
	marketChildren := make([]map[string]any, 0, 4)
	if HasPermission(account, PermissionMarketplaceRead) || HasPermission(account, PermissionMarketplaceManage) {
		marketChildren = append(marketChildren, map[string]any{"name": "Marketplace", "path": "overview", "component": "/gateway/marketplace", "meta": map[string]any{"title": "市场总览"}})
	}
	if HasPermission(account, PermissionMCPServerRead) || HasPermission(account, PermissionMCPServerManage) {
		marketChildren = append(marketChildren, map[string]any{"name": "MCPServers", "path": "mcp-servers", "component": "/gateway/mcp-servers", "meta": map[string]any{"title": "MCP 服务器"}})
	}
	if HasPermission(account, PermissionSkillRead) || HasPermission(account, PermissionSkillManage) {
		marketChildren = append(marketChildren, map[string]any{"name": "Skills", "path": "skills", "component": "/gateway/skills", "meta": map[string]any{"title": "Skills 技能"}})
	}
	if HasPermission(account, PermissionDigitalEmployeeRead) || HasPermission(account, PermissionDigitalEmployeeManage) {
		marketChildren = append(marketChildren, map[string]any{"name": "DigitalEmployees", "path": "digital-employees", "component": "/gateway/digital-employees", "meta": map[string]any{"title": "数字员工"}})
	}
	if len(marketChildren) > 0 {
		menus = append(menus, map[string]any{"name": "ResourceMarket", "path": "/resource-market", "component": "/index/index", "meta": map[string]any{"title": "资源市场", "icon": "ri:store-3-line"}, "children": marketChildren})
	}

	// 系统管理：账号权限、事件投递与通知。
	systemChildren := make([]map[string]any, 0, 3)
	if HasPermission(account, PermissionIdentityManage) {
		systemChildren = append(systemChildren, map[string]any{"name": "User", "path": "user", "component": "/system/user", "meta": map[string]any{"title": "账号与权限"}})
	}
	if HasPermission(account, PermissionOutboxRead) || HasPermission(account, PermissionOutboxManage) {
		systemChildren = append(systemChildren, map[string]any{"name": "Outbox", "path": "outbox", "component": "/gateway/outbox", "meta": map[string]any{"title": "事件投递"}})
	}
	if HasPermission(account, PermissionNotificationRead) || HasPermission(account, PermissionNotificationManage) {
		systemChildren = append(systemChildren, map[string]any{"name": "Notifications", "path": "notifications", "component": "/gateway/notifications", "meta": map[string]any{"title": "通知中心"}})
	}
	if len(systemChildren) > 0 {
		menus = append(menus, map[string]any{"name": "System", "path": "/system", "component": "/index/index", "meta": map[string]any{"title": "系统管理", "icon": "ri:user-3-line"}, "children": systemChildren})
	}
	return menus
}

func portalMenus() []map[string]any {
	return []map[string]any{
		{"name": "Portal", "path": "/portal", "component": "/index/index", "meta": map[string]any{"title": "AI 工作台", "icon": "ri:sparkling-line"}, "children": []map[string]any{
			{"name": "PortalCatalog", "path": "catalog", "component": "/portal/catalog", "meta": map[string]any{"title": "资产目录", "fixedTab": true}},
			{"name": "PortalMarketplace", "path": "marketplace", "component": "/portal/marketplace", "meta": map[string]any{"title": "资源市场"}},
			{"name": "PortalPrompts", "path": "prompts", "component": "/portal/prompts", "meta": map[string]any{"title": "Prompt 广场"}},
			{"name": "PortalUsage", "path": "usage", "component": "/portal/usage", "meta": map[string]any{"title": "我的用量"}},
			{"name": "PortalAccess", "path": "access", "component": "/portal/access", "meta": map[string]any{"title": "模型权限"}},
			{"name": "PortalFiles", "path": "files", "component": "/portal/files", "meta": map[string]any{"title": "文件仓库"}},
		}},
	}
}
