package agent

import (
	"context"
	"errors"
	"log/slog"
	"net/http"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"security-center-backend/internal/model"
	"security-center-backend/internal/pkg/response"
)

// whoamiUserLookup abstracts the users table lookup behind Whoami so tests
// can swap in a fake (same pattern as usageStatsQuerier in usage.go).
type whoamiUserLookup interface {
	FindUserByID(ctx context.Context, id uint64) (*model.User, error)
}

// gormWhoamiUserLookup is the production MySQL-backed implementation.
type gormWhoamiUserLookup struct {
	db *gorm.DB
}

func newGormWhoamiUserLookup(db *gorm.DB) *gormWhoamiUserLookup {
	return &gormWhoamiUserLookup{db: db}
}

// FindUserByID returns (nil, nil) when the user row is gone (e.g. the
// credential outlived the account), letting Whoami degrade to claims-only
// fields instead of failing a read-only introspection endpoint.
func (q *gormWhoamiUserLookup) FindUserByID(ctx context.Context, id uint64) (*model.User, error) {
	var user model.User
	err := q.db.WithContext(ctx).First(&user, id).Error
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}
	return &user, nil
}

// WhoamiResponse is the contract for GET /agent/whoami: the caller's
// identity as the platform sees it. Agent-only fields (agent_id,
// agent_name, permission_scope) are omitted for human JWT callers;
// employee_id is always present and null until the user is linked to an
// employee record (owner/fixer filtering depends on it).
type WhoamiResponse struct {
	ActorType       string   `json:"actor_type"` // "agent" | "user"
	AgentID         uint64   `json:"agent_id,omitempty"`
	AgentName       string   `json:"agent_name,omitempty"`
	UserID          uint64   `json:"user_id"`
	Name            string   `json:"name,omitempty"`
	Email           string   `json:"email,omitempty"`
	EmployeeID      *uint64  `json:"employee_id"`
	PermissionScope []string `json:"permission_scope,omitempty"`
}

// Whoami handles GET /agent/whoami: it echoes the authenticated identity
// (agent API key or human JWT) enriched with the users row, so MCP/LLM
// callers can resolve "我/我的" queries (submitted-by-me → user_id,
// owned-by-me → employee_id, my key's capability → permission_scope). The
// route hangs on the protected chain without the agent:manage_own_keys
// Permission middleware — any authenticated principal may call it. The
// response carries no key material, only identity metadata.
func (h *Handler) Whoami(c *gin.Context) {
	claims := currentUser(c)
	if claims == nil {
		response.Error(c, http.StatusUnauthorized, "not authenticated")
		return
	}

	resp := &WhoamiResponse{
		ActorType: "user",
		UserID:    claims.UserID,
		Email:     claims.Email,
	}
	if claims.ActorType == "agent" {
		resp.ActorType = "agent"
		resp.AgentID = claims.AgentID
		resp.AgentName = claims.AgentName
		resp.PermissionScope = append([]string(nil), claims.Permissions...)
	}

	// name/email/employee_id 以 users 表为准（agent claims 只有 user_id）。
	// h.users 为空（无 db 的测试构造）或用户行缺失时降级为 claims 字段。
	if h.users != nil {
		user, err := h.users.FindUserByID(c.Request.Context(), claims.UserID)
		if err != nil {
			slog.Warn("whoami: user lookup failed", "user_id", claims.UserID, "error", err)
			response.Error(c, http.StatusInternalServerError, "failed to load user")
			return
		}
		if user != nil {
			resp.Name = user.Name
			resp.Email = user.Email
			resp.EmployeeID = user.EmployeeID
		}
	}

	response.Success(c, resp)
}
