// handler.go 暴露 admin-only 的审计日志查看接口（spec §11 步骤4）。
//
//	GET /api/v1/admin/audit-logs?action=&action_prefix=&target_type=&target_id=
//	    &user_id=&from=&to=&limit=&offset=
//
// 鉴权：复刻 chat.adminGuard —— Auth 中间件解出 uid，再经 AdminLookup.IsAdmin
// 门禁；非 admin 返回 403。from/to 接受 RFC3339 时间字符串。
package audit

import (
	"context"
	"net/http"
	"strconv"
	"time"

	"github.com/go-chi/chi/v5"
	"github.com/google/uuid"

	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
	httpx "github.com/yan1h/agent-coding-workflow/internal/transport/http"
	"github.com/yan1h/agent-coding-workflow/internal/transport/http/middleware"
)

// AdminLookup resolves whether a user is an admin (narrow interface; mirrors
// chat/acp handler pattern). user.Service satisfies it.
type AdminLookup interface {
	IsAdmin(ctx context.Context, id uuid.UUID) (bool, error)
}

// Handler exposes the admin audit-log read endpoint.
type Handler struct {
	reader   Reader
	resolver middleware.SessionResolver
	admin    AdminLookup
}

// NewHandler constructs the audit read handler.
func NewHandler(reader Reader, resolver middleware.SessionResolver, admin AdminLookup) *Handler {
	return &Handler{reader: reader, resolver: resolver, admin: admin}
}

// Mount registers the admin audit routes under /api/v1/admin.
func (h *Handler) Mount(r chi.Router) {
	r.Route("/api/v1/admin/audit-logs", func(r chi.Router) {
		r.Use(middleware.Auth(h.resolver, middleware.AuthOptions{}))
		r.Use(h.adminGuard)
		r.Get("/", h.list)
	})
}

func (h *Handler) adminGuard(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		uid, ok := middleware.UserIDFromContext(r.Context())
		if !ok {
			h.writeErr(w, r, errs.New(errs.CodeUnauthorized, "unauthorized"))
			return
		}
		isAdmin, err := h.admin.IsAdmin(r.Context(), uid)
		if err != nil {
			h.writeErr(w, r, err)
			return
		}
		if !isAdmin {
			h.writeErr(w, r, errs.New(errs.CodeForbidden, "admin only"))
			return
		}
		next.ServeHTTP(w, r)
	})
}

type auditLogDTO struct {
	ID         int64          `json:"id"`
	UserID     *string        `json:"user_id,omitempty"`
	Action     string         `json:"action"`
	TargetType string         `json:"target_type,omitempty"`
	TargetID   string         `json:"target_id,omitempty"`
	IP         string         `json:"ip,omitempty"`
	Metadata   map[string]any `json:"metadata,omitempty"`
	CreatedAt  string         `json:"created_at"`
}

func (h *Handler) list(w http.ResponseWriter, r *http.Request) {
	q := r.URL.Query()
	f := AuditFilter{
		Action:       q.Get("action"),
		ActionPrefix: q.Get("action_prefix"),
		TargetType:   q.Get("target_type"),
		TargetID:     q.Get("target_id"),
	}
	if v := q.Get("user_id"); v != "" {
		uid, err := uuid.Parse(v)
		if err != nil {
			h.writeErr(w, r, errs.New(errs.CodeInvalidInput, "user_id 格式错误"))
			return
		}
		f.UserID = &uid
	}
	if v := q.Get("from"); v != "" {
		t, err := time.Parse(time.RFC3339, v)
		if err != nil {
			h.writeErr(w, r, errs.New(errs.CodeInvalidInput, "from 须为 RFC3339"))
			return
		}
		f.From = &t
	}
	if v := q.Get("to"); v != "" {
		t, err := time.Parse(time.RFC3339, v)
		if err != nil {
			h.writeErr(w, r, errs.New(errs.CodeInvalidInput, "to 须为 RFC3339"))
			return
		}
		f.To = &t
	}
	if v := q.Get("limit"); v != "" {
		n, err := strconv.Atoi(v)
		if err != nil || n < 0 {
			h.writeErr(w, r, errs.New(errs.CodeInvalidInput, "limit 须为非负整数"))
			return
		}
		f.Limit = n
	}
	if v := q.Get("offset"); v != "" {
		n, err := strconv.Atoi(v)
		if err != nil || n < 0 {
			h.writeErr(w, r, errs.New(errs.CodeInvalidInput, "offset 须为非负整数"))
			return
		}
		f.Offset = n
	}

	entries, total, err := h.reader.List(r.Context(), f)
	if err != nil {
		h.writeErr(w, r, err)
		return
	}
	out := make([]auditLogDTO, 0, len(entries))
	for _, e := range entries {
		dto := auditLogDTO{
			ID:         e.ID,
			Action:     e.Action,
			TargetType: e.TargetType,
			TargetID:   e.TargetID,
			IP:         e.IP,
			Metadata:   e.Metadata,
			CreatedAt:  e.CreatedAt.UTC().Format(time.RFC3339),
		}
		if e.UserID != nil {
			s := e.UserID.String()
			dto.UserID = &s
		}
		out = append(out, dto)
	}
	httpx.WriteJSON(w, http.StatusOK, map[string]any{"items": out, "total": total})
}

func (h *Handler) writeErr(w http.ResponseWriter, r *http.Request, err error) {
	httpx.WriteError(w, middleware.RequestIDFromContext(r.Context()), err)
}
