package authx

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/golang-jwt/jwt/v4"
	"github.com/zeromicro/go-zero/rest/httpx"
)

type ctxKey string

const (
	CtxTenantID  ctxKey = "tenant_id"
	CtxUserID    ctxKey = "user_id"
	CtxRole      ctxKey = "role"
	CtxAgentID   ctxKey = "agent_id"
	CtxPerms     ctxKey = "perms"
	CtxOrgUnitID ctxKey = "org_unit_id"
)

// 平台级角色 → 权限映射（角色、权限一律中文命名）
var RolePermissions = map[string][]string{
	Role超级管理员: {
		Perm管理租户, // 只管公司一级：列表/新建等；不介入公司内模块、成员、同步
	},
	Role管理员: {
		Perm读取模块, Perm写入模块, Perm发布模块,
		Perm新增数据, Perm查询数据, Perm更新数据, Perm删除数据, Perm导出数据, Perm导入数据,
		Perm查看审计, Perm上传文件, Perm下载文件,
		Perm管理智能体, Perm邀请成员, Perm管理组织,
		Perm数据同步,
	},
	Role编辑: {
		Perm读取模块, Perm写入模块,
		Perm新增数据, Perm查询数据, Perm更新数据, Perm导出数据, Perm导入数据,
		Perm上传文件, Perm下载文件,
	},
	Role只读: {
		Perm读取模块, Perm查询数据, Perm导出数据, Perm下载文件, Perm查看审计,
	},
	Role待加入: {},
}

type Claims struct {
	TenantID  int64    `json:"tenant_id"`
	UserID    int64    `json:"user_id"`
	Role      string   `json:"role,omitempty"`
	OrgUnitID int64    `json:"org_unit_id,omitempty"`
	AgentID   int64    `json:"agent_id,omitempty"`
	Perms     []string `json:"perms,omitempty"`
	jwt.RegisteredClaims
}

type JWTConfig struct {
	AccessSecret string
	AccessExpire int64
}

func (c JWTConfig) Expire() time.Duration {
	if c.AccessExpire <= 0 {
		return 24 * time.Hour
	}
	return time.Duration(c.AccessExpire) * time.Second
}

func IssueToken(cfg JWTConfig, tenantID, userID int64, role string, orgUnitID int64) (string, int64, error) {
	if cfg.AccessSecret == "" {
		return "", 0, errors.New("auth access secret empty")
	}
	if userID <= 0 {
		return "", 0, errors.New("user_id required")
	}
	if role == "" {
		role = Role管理员
	}
	role = NormalizeRole(role)
	if _, ok := RolePermissions[role]; !ok {
		return "", 0, fmt.Errorf("unknown role: %s", role)
	}
	// 待加入 / 超级管理员可无租户；其他角色必须带 tenant_id
	if tenantID <= 0 && role != Role待加入 && role != Role超级管理员 {
		return "", 0, errors.New("tenant_id and user_id required")
	}
	if tenantID < 0 {
		tenantID = 0
	}
	return signClaims(cfg, Claims{
		TenantID:  tenantID,
		UserID:    userID,
		Role:      role,
		OrgUnitID: orgUnitID,
	})
}

// IssueAgentToken 签发智能体 JWT（role=agent，权限写在 perms 声明中）。
func IssueAgentToken(cfg JWTConfig, tenantID, agentID int64, perms []string) (string, int64, error) {
	if cfg.AccessSecret == "" {
		return "", 0, errors.New("auth access secret empty")
	}
	if tenantID <= 0 || agentID <= 0 {
		return "", 0, errors.New("tenant_id and agent_id required")
	}
	return signClaims(cfg, Claims{
		TenantID: tenantID,
		UserID:   agentID,
		Role:     RoleAgent,
		AgentID:  agentID,
		Perms:    NormalizePerms(perms),
	})
}

func signClaims(cfg JWTConfig, claims Claims) (string, int64, error) {
	exp := time.Now().Add(cfg.Expire())
	claims.RegisteredClaims = jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(exp),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
		Issuer:    "aijianzhan-platform",
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	signed, err := token.SignedString([]byte(cfg.AccessSecret))
	if err != nil {
		return "", 0, err
	}
	return signed, exp.Unix(), nil
}

func ParseToken(cfg JWTConfig, tokenStr string) (*Claims, error) {
	if cfg.AccessSecret == "" {
		return nil, errors.New("auth access secret empty")
	}
	parser := jwt.NewParser(jwt.WithValidMethods([]string{"HS256"}))
	token, err := parser.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (any, error) {
		return []byte(cfg.AccessSecret), nil
	})
	if err != nil {
		return nil, err
	}
	claims, ok := token.Claims.(*Claims)
	if !ok || !token.Valid {
		return nil, errors.New("invalid token")
	}
	if claims.UserID <= 0 {
		return nil, errors.New("token missing user")
	}
	if claims.Role == "" {
		claims.Role = Role只读
	}
	claims.Role = NormalizeRole(claims.Role)
	if claims.TenantID <= 0 && claims.Role != Role待加入 && claims.Role != Role超级管理员 {
		return nil, errors.New("token missing tenant/user")
	}
	if claims.TenantID < 0 {
		claims.TenantID = 0
	}
	if claims.Role == Role智能体 && claims.AgentID <= 0 {
		claims.AgentID = claims.UserID
	}
	return claims, nil
}

func Middleware(jwtCfg JWTConfig, devAuth bool) func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			auth := r.Header.Get("Authorization")
			if strings.HasPrefix(strings.ToLower(auth), "bearer ") {
				raw := strings.TrimSpace(auth[7:])
				claims, err := ParseToken(jwtCfg, raw)
				if err != nil {
					WriteError(w, http.StatusUnauthorized, "invalid token: "+err.Error())
					return
				}
				ctx := WithFullClaims(r.Context(), claims.TenantID, claims.UserID, claims.Role, claims.AgentID, claims.OrgUnitID, claims.Perms)
				next(w, r.WithContext(ctx))
				return
			}

			if devAuth {
				tenantID, _ := strconv.ParseInt(r.Header.Get("X-Tenant-Id"), 10, 64)
				userID, _ := strconv.ParseInt(r.Header.Get("X-User-Id"), 10, 64)
				role := r.Header.Get("X-Role")
				if tenantID <= 0 {
					tenantID = 1
				}
				if userID <= 0 {
					userID = 1
				}
				if role == "" {
					role = Role管理员
				}
				role = NormalizeRole(role)
				ctx := WithClaims(r.Context(), tenantID, userID, role)
				next(w, r.WithContext(ctx))
				return
			}

			WriteError(w, http.StatusUnauthorized, "missing Authorization Bearer token")
		}
	}
}

// RequirePermission 校验平台角色或智能体 JWT 内嵌权限。
func RequirePermission(perm string) func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if !HasPermissionCtx(r.Context(), perm) {
				role := Role(r.Context())
				WriteError(w, http.StatusForbidden, "permission denied: "+perm+" (role="+role+")")
				return
			}
			next(w, r)
		}
	}
}

// RequireSyncPushAccess 放行「数据同步」权限，或已登录人类成员（用户自助 sync）。
// 智能体无「数据同步」仍 403；无 sync 权的人类由 handler 再按 Binding 收窄范围。
func RequireSyncPushAccess() func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if HasPermissionCtx(r.Context(), Perm数据同步) {
				next(w, r)
				return
			}
			if IsAgent(Role(r.Context())) {
				WriteError(w, http.StatusForbidden, "permission denied: 数据同步 (role=智能体)")
				return
			}
			if UserID(r.Context()) <= 0 {
				WriteError(w, http.StatusForbidden, "permission denied: need user login or 数据同步")
				return
			}
			next(w, r)
		}
	}
}

// SyncTenantWide 是否租户级同步权（管理员/智能体带「数据同步」）；否则须按 Binding 自助范围。
func SyncTenantWide(ctx context.Context) bool {
	return HasPermissionCtx(ctx, Perm数据同步)
}

// RequireTenant 要求已加入租户；pending / 无 tenant 不可访问业务数据。
// 超级管理员可在「进入某公司」后携带 tenant_id 访问公司内接口（可看可改，前端会多重确认）。
func RequireTenant() func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if Role(r.Context()) == Role智能体 {
				if TenantID(r.Context()) <= 0 {
					WriteError(w, http.StatusForbidden, "agent missing tenant")
					return
				}
				next(w, r)
				return
			}
			if Role(r.Context()) == Role超级管理员 {
				if TenantID(r.Context()) <= 0 {
					WriteError(w, http.StatusForbidden, "请先在平台管理中进入某公司后再操作公司内部功能")
					return
				}
				next(w, r)
				return
			}
			if TenantID(r.Context()) <= 0 || Role(r.Context()) == Role待加入 {
				WriteError(w, http.StatusForbidden, "join a tenant first (pending membership)")
				return
			}
			next(w, r)
		}
	}
}

// RequirePlatformAdmin 仅平台超级管理员。
func RequirePlatformAdmin() func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if !IsPlatformAdmin(Role(r.Context())) {
				WriteError(w, http.StatusForbidden, "需要超级管理员")
				return
			}
			next(w, r)
		}
	}
}

// RequireTenantBound 保留兼容：业务接口要求已绑定租户。
func RequireTenantBound() func(http.HandlerFunc) http.HandlerFunc {
	return func(next http.HandlerFunc) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			if IsPlatformAdmin(Role(r.Context())) {
				if TenantID(r.Context()) <= 0 {
					WriteError(w, http.StatusForbidden, "请先进入某公司")
					return
				}
				next(w, r)
				return
			}
			if TenantID(r.Context()) <= 0 {
				WriteError(w, http.StatusForbidden, "请先加入公司")
				return
			}
			next(w, r)
		}
	}
}

func HasPermission(role, perm string) bool {
	perms, ok := RolePermissions[NormalizeRole(role)]
	if !ok {
		return false
	}
	want := NormalizePerm(perm)
	for _, p := range perms {
		if NormalizePerm(p) == want {
			return true
		}
	}
	return false
}

func HasPermissionCtx(ctx context.Context, perm string) bool {
	role := NormalizeRole(Role(ctx))
	want := NormalizePerm(perm)
	if role == Role超级管理员 {
		// 进入公司后：超管可使用全部公司权限（平台专属「管理租户」仍保留）
		if want == Perm管理租户 {
			return true
		}
		for _, p := range CompanyPermCatalog() {
			if NormalizePerm(p) == want {
				return TenantID(ctx) > 0
			}
		}
		return HasPermission(role, want)
	}
	if role == Role智能体 {
		ok := false
		for _, p := range Perms(ctx) {
			if NormalizePerm(p) == want {
				ok = true
				break
			}
		}
		if !ok {
			return false
		}
	} else if !HasPermission(role, want) {
		return false
	}
	tid := TenantID(ctx)
	if tid > 0 && EntitlementChecker != nil {
		if !EntitlementChecker(ctx, tid, want) {
			return false
		}
	}
	return true
}

func withClaims(ctx context.Context, tenantID, userID int64, role string) context.Context {
	return WithClaims(ctx, tenantID, userID, role)
}

// WithClaims 写入租户/用户/角色（公开展示等场景用）。
func WithClaims(ctx context.Context, tenantID, userID int64, role string) context.Context {
	return WithFullClaims(ctx, tenantID, userID, role, 0, 0, nil)
}

func WithFullClaims(ctx context.Context, tenantID, userID int64, role string, agentID, orgUnitID int64, perms []string) context.Context {
	ctx = context.WithValue(ctx, CtxTenantID, tenantID)
	ctx = context.WithValue(ctx, CtxUserID, userID)
	ctx = context.WithValue(ctx, CtxRole, role)
	ctx = context.WithValue(ctx, CtxAgentID, agentID)
	ctx = context.WithValue(ctx, CtxOrgUnitID, orgUnitID)
	if perms != nil {
		ctx = context.WithValue(ctx, CtxPerms, append([]string{}, perms...))
	}
	return ctx
}

func TenantID(ctx context.Context) int64 {
	v, _ := ctx.Value(CtxTenantID).(int64)
	return v
}

func UserID(ctx context.Context) int64 {
	v, _ := ctx.Value(CtxUserID).(int64)
	return v
}

func AgentID(ctx context.Context) int64 {
	v, _ := ctx.Value(CtxAgentID).(int64)
	return v
}

func OrgUnitID(ctx context.Context) int64 {
	v, _ := ctx.Value(CtxOrgUnitID).(int64)
	return v
}

func Role(ctx context.Context) string {
	v, _ := ctx.Value(CtxRole).(string)
	if v == "" {
		return Role只读
	}
	return NormalizeRole(v)
}

func Perms(ctx context.Context) []string {
	v, _ := ctx.Value(CtxPerms).([]string)
	return v
}

func WriteError(w http.ResponseWriter, code int, msg string) {
	httpx.WriteJson(w, code, map[string]any{
		"code":    code,
		"message": msg,
	})
}

// WriteRetryableError 供 agent 识别可稍后重试（不换 UUID）。
func WriteRetryableError(w http.ResponseWriter, code int, msg string) {
	httpx.WriteJson(w, code, map[string]any{
		"code":      code,
		"message":   msg,
		"retryable": true,
	})
}
