package audit

import (
	"encoding/csv"
	"net/http"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"

	"security-center-backend/internal/pkg/response"
	"security-center-backend/internal/repository"
	"security-center-backend/internal/service/audit"
)

type Handler struct {
	service *audit.Service
}

func NewHandler(service *audit.Service) *Handler {
	return &Handler{service: service}
}

func (h *Handler) List(c *gin.Context) {
	page := queryInt(c, "page", 1)
	pageSize := queryInt(c, "page_size", 20)
	filter, err := buildFilterFromRequest(c)
	if err != nil {
		response.Error(c, http.StatusBadRequest, err.Error())
		return
	}
	logs, total, err := h.service.List(c.Request.Context(), filter, page, pageSize)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	payload := response.PaginatedResponse{
		Items:      logs,
		Total:      total,
		Page:       page,
		PageSize:   pageSize,
		TotalPages: calculateTotalPages(total, pageSize),
	}
	response.Success(c, payload)
}

func (h *Handler) Get(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.Error(c, http.StatusBadRequest, "invalid id")
		return
	}
	logEntry, err := h.service.Get(c.Request.Context(), id)
	if err != nil {
		response.Error(c, http.StatusNotFound, err.Error())
		return
	}
	response.Success(c, logEntry)
}

type exportRequest struct {
	UserID       *uint64 `json:"user_id"`
	ResourceType string  `json:"resource_type"`
	Action       string  `json:"action"`
	From         string  `json:"from"`
	To           string  `json:"to"`
}

func (h *Handler) Export(c *gin.Context) {
	var req exportRequest
	if err := c.ShouldBindJSON(&req); err != nil && err.Error() != "EOF" {
		response.Error(c, http.StatusBadRequest, "invalid request body")
		return
	}
	filter := repository.AuditFilter{ResourceType: req.ResourceType, Action: req.Action}
	if req.UserID != nil {
		filter.UserID = req.UserID
	}
	if req.From != "" {
		if ts, err := time.Parse(time.RFC3339, req.From); err == nil {
			filter.From = &ts
		}
	}
	if req.To != "" {
		if ts, err := time.Parse(time.RFC3339, req.To); err == nil {
			filter.To = &ts
		}
	}
	logs, _, err := h.service.List(c.Request.Context(), filter, 1, 1000)
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	c.Header("Content-Type", "text/csv")
	c.Header("Content-Disposition", "attachment; filename=audit-logs.csv")
	w := csv.NewWriter(c.Writer)
	_ = w.Write([]string{"id", "user_id", "action", "resource_type", "resource_id", "created_at"})
	for _, logEntry := range logs {
		userID := ""
		if logEntry.UserID != nil {
			userID = strconv.FormatUint(*logEntry.UserID, 10)
		}
		_ = w.Write([]string{
			strconv.FormatUint(logEntry.ID, 10),
			userID,
			logEntry.Action,
			logEntry.ResourceType,
			logEntry.ResourceID,
			logEntry.CreatedAt.Format(time.RFC3339),
		})
	}
	w.Flush()
	if err := w.Error(); err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
	}
}

func (h *Handler) Stats(c *gin.Context) {
	stats, err := h.service.Stats(c.Request.Context())
	if err != nil {
		response.Error(c, http.StatusInternalServerError, err.Error())
		return
	}
	response.Success(c, stats)
}

func buildFilterFromRequest(c *gin.Context) (repository.AuditFilter, error) {
	filter := repository.AuditFilter{
		ResourceType: c.Query("resource_type"),
		Action:       c.Query("action"),
		Keyword:      c.Query("keyword"),
		ActorEmail:   c.Query("actor_email"),
		DepartmentID: c.Query("department_id"),
	}
	if actorType := c.Query("actor_type"); actorType != "" {
		switch actorType {
		case "user", "agent", "system":
			filter.ActorType = actorType
		}
	}
	if userID := c.Query("user_id"); userID != "" {
		value, err := strconv.ParseUint(userID, 10, 64)
		if err != nil {
			return filter, err
		}
		filter.UserID = &value
	}
	if from := c.Query("from"); from != "" {
		if ts, err := time.Parse(time.RFC3339, from); err == nil {
			filter.From = &ts
		}
	}
	if to := c.Query("to"); to != "" {
		if ts, err := time.Parse(time.RFC3339, to); err == nil {
			filter.To = &ts
		}
	}
	return filter, nil
}

func queryInt(c *gin.Context, key string, fallback int) int {
	value := c.Query(key)
	if value == "" {
		return fallback
	}
	parsed, err := strconv.Atoi(value)
	if err != nil || parsed <= 0 {
		return fallback
	}
	return parsed
}

func calculateTotalPages(total int64, pageSize int) int {
	if pageSize <= 0 {
		return 0
	}
	pages := total / int64(pageSize)
	if total%int64(pageSize) != 0 {
		pages++
	}
	return int(pages)
}
