package handler

import (
	"errors"
	"log/slog"
	"strconv"
	"time"

	"kra/internal/biz"
	"kra/internal/service"
	"kra/internal/service/dto"

	"github.com/gin-gonic/gin"
)

type Audit struct {
	service  *service.AuditService
	recorder *service.AuditRecorder
	logs     *service.LogViewerService
	logger   *slog.Logger
}

func NewAudit(service *service.AuditService, recorder *service.AuditRecorder, logs *service.LogViewerService, logger *slog.Logger) *Audit {
	return &Audit{service: service, recorder: recorder, logs: logs, logger: logger}
}

func (h *Audit) Operations(c *gin.Context) {
	var req dto.OperationRecordSearchRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	items, total, err := h.service.OperationsFilter(c.Request.Context(), req.Page, req.PageSize, req.Path, req.Method, req.Status)
	if err != nil {
		Fail(c, "获取失败")
		return
	}
	Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) Operation(c *gin.Context) {
	var req dto.AuditIDQuery
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if req.ID == 0 {
		Fail(c, "ID值不能为空")
		return
	}
	item, err := h.service.Operation(c.Request.Context(), req.ID)
	if err != nil {
		Fail(c, "查询失败")
		return
	}
	Write(c, CodeSuccess, gin.H{"reSysOperationRecord": item}, "查询成功")
}
func (h *Audit) DeleteOperation(c *gin.Context) {
	var req dto.IDRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.DeleteOperations(c.Request.Context(), []int{int(req.ID)}); err != nil {
		Fail(c, "删除失败")
		return
	}
	Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteOperations(c *gin.Context) {
	var req dto.AuditIDsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.DeleteOperations(c.Request.Context(), req.IDs); err != nil {
		Fail(c, "批量删除失败")
		return
	}
	Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}

func (h *Audit) Logins(c *gin.Context) {
	var req dto.LoginLogSearchRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	items, total, err := h.service.LoginsFilter(c.Request.Context(), req.Page, req.PageSize, req.Username, req.Status)
	if err != nil {
		Fail(c, "获取失败")
		return
	}
	Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) Login(c *gin.Context) {
	var req dto.AuditIDQuery
	if err := c.ShouldBindQuery(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	item, err := h.service.Login(c.Request.Context(), req.ID)
	if err != nil {
		Fail(c, "查询失败")
		return
	}
	Write(c, CodeSuccess, item, "查询成功")
}
func (h *Audit) DeleteLogin(c *gin.Context) {
	var req dto.IDRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.DeleteLogins(c.Request.Context(), []int{int(req.ID)}); err != nil {
		Fail(c, "删除失败")
		return
	}
	Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteLogins(c *gin.Context) {
	var req dto.AuditIDsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.DeleteLogins(c.Request.Context(), req.IDs); err != nil {
		Fail(c, "批量删除失败")
		return
	}
	Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}

func (h *Audit) DataAccess(c *gin.Context) {
	var req dto.DataAccessListRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	items, total, err := h.service.DataAccessRequest(c.Request.Context(), &req)
	if err != nil {
		Fail(c, "获取失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) DeleteDataAccess(c *gin.Context) {
	var req dto.IDsRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.DeleteDataAccess(c.Request.Context(), req.IDs); err != nil {
		Fail(c, "删除失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, gin.H{}, "删除成功")
}

func (h *Audit) LogDates(c *gin.Context) {
	month := c.Query("month")
	// Month uses `binding:"required"`; an omitted or empty query is a
	// generic parameter error, not the service's format-specific error.
	if month == "" {
		Fail(c, "参数错误")
		return
	}
	data, err := h.logs.LogDates(c.Request.Context(), month)
	if err != nil {
		failLogViewer(c, err, h.logger)
		return
	}
	Write(c, CodeSuccess, data, "获取成功")
}
func (h *Audit) LogFiles(c *gin.Context) {
	date := c.Query("date")
	if date == "" {
		Fail(c, "参数错误")
		return
	}
	data, err := h.logs.LogFiles(c.Request.Context(), date)
	if err != nil {
		failLogViewer(c, err, h.logger)
		return
	}
	Write(c, CodeSuccess, data, "获取成功")
}
func (h *Audit) LogContent(c *gin.Context) {
	date, path := c.Query("date"), c.Query("path")
	if date == "" || path == "" {
		Fail(c, "参数错误")
		return
	}
	var cursor *int64
	if raw := c.Query("cursor"); raw != "" {
		value, err := strconv.ParseInt(raw, 10, 64)
		if err != nil {
			Fail(c, "参数错误")
			return
		}
		cursor = &value
	}
	// Preserve negative cursors for the usecase to classify with the compatible
	// "日志文件路径不合法" error rather than rejecting them as binding errors.
	data, err := h.logs.LogContent(c.Request.Context(), date, path, cursor)
	if err != nil {
		failLogViewer(c, err, h.logger)
		return
	}
	Write(c, CodeSuccess, data, "获取成功")
}

func failLogViewer(c *gin.Context, err error, logger *slog.Logger) {
	if logger != nil {
		logger.ErrorContext(c.Request.Context(), "日志查看失败", "mod", "log-viewer", "error", err)
	}
	message := "读取日志失败"
	switch {
	case errors.Is(err, biz.ErrInvalidLogMonth):
		message = "日志月份格式不正确"
	case errors.Is(err, biz.ErrInvalidLogDate):
		message = "日志日期格式不正确"
	case errors.Is(err, biz.ErrInvalidLogPath):
		message = "日志文件路径不合法"
	case errors.Is(err, biz.ErrLogFileNotFound):
		message = "日志文件不存在"
	case errors.Is(err, biz.ErrLogFileUnreadable):
		message = "日志文件不可读取"
	case errors.Is(err, biz.ErrLogRootUnavailable):
		message = "日志目录不可读取"
	}
	Fail(c, message)
}

func (h *Audit) DeleteError(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
	if err := h.service.DeleteErrors(c.Request.Context(), []uint{uint(id)}); err != nil {
		Fail(c, "删除失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteErrors(c *gin.Context) {
	if err := h.service.DeleteErrors(c.Request.Context(), IDsFromQuery(c)); err != nil {
		Fail(c, "批量删除失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Audit) UpdateError(c *gin.Context) {
	var req dto.ErrorRecordMutationRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.service.UpdateErrorRequest(c.Request.Context(), &req); err != nil {
		Fail(c, "更新失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Audit) Error(c *gin.Context) {
	id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
	item, err := h.service.Error(c.Request.Context(), uint(id))
	if err != nil {
		Fail(c, "查询失败:"+err.Error())
		return
	}
	OKWithData(c, item)
}
func (h *Audit) Errors(c *gin.Context) {
	p, size, err := page(c)
	if err != nil {
		Fail(c, err.Error())
		return
	}
	createdAtRange := make([]time.Time, 0, 2)
	for _, raw := range c.QueryArray("createdAtRange[]") {
		value, parseErr := time.Parse(time.RFC3339, raw)
		if parseErr != nil {
			Fail(c, parseErr.Error())
			return
		}
		createdAtRange = append(createdAtRange, value)
	}
	items, total, err := h.service.ErrorsFilter(c.Request.Context(), p, size, c.Query("form"), c.Query("info"), createdAtRange)
	if err != nil {
		Fail(c, "获取失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
}
func (h *Audit) CreateError(c *gin.Context) {
	var req dto.ErrorRecordMutationRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Fail(c, err.Error())
		return
	}
	if err := h.recorder.CreateErrorMutationRequest(c.Request.Context(), &req); err != nil {
		Fail(c, "创建失败:"+err.Error())
		return
	}
	Write(c, CodeSuccess, gin.H{}, "创建成功")
}
