package dialectsfcrm

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

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
)

type adminAuditLogRow struct {
	ID               string  `json:"Id"`
	ObjectAPIName    string  `json:"ObjectApiName"`
	RecordID         string  `json:"RecordId"`
	FieldName        string  `json:"FieldName"`
	OldValue         *string `json:"OldValue,omitempty"`
	NewValue         *string `json:"NewValue,omitempty"`
	ChangedByID      string  `json:"ChangedById"`
	ChangedAt        string  `json:"ChangedAt"`
	Action           string  `json:"Action,omitempty"`
	ChangesJSON      string  `json:"ChangesJSON,omitempty"`
	Entrypoint       string  `json:"Entrypoint,omitempty"`
	EntrypointDetail string  `json:"EntrypointDetail,omitempty"`
	RequestID        string  `json:"RequestId,omitempty"`
	TraceID          string  `json:"TraceId,omitempty"`
	SessionID        string  `json:"SessionId,omitempty"`
	ClientIP         string  `json:"ClientIp,omitempty"`
	ActualUserID     string  `json:"ActualUserId,omitempty"`
	Outcome          string  `json:"Outcome,omitempty"`
	ErrorCode        string  `json:"ErrorCode,omitempty"`
}

type adminAuditLogResponse struct {
	Records []adminAuditLogRow `json:"records"`
	Count   int                `json:"count"`
	Limit   int                `json:"limit"`
	Offset  int                `json:"offset"`
	Object  string             `json:"object"`
}

// AdminAuditLogNeutralHandler handles GET /admin/audit/log.
func AdminAuditLogNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	auditLog := deps.Contracts.AuditLog
	if auditLog == nil {
		return &dialect.Response{Status: http.StatusServiceUnavailable, Body: map[string]any{"error": "audit log backend not initialized"}}, nil
	}

	object := firstQueryValue(req, "object")
	recordID := firstQueryValue(req, "recordId")
	if object == "" || recordID == "" {
		return &dialect.Response{Status: http.StatusBadRequest, Body: map[string]any{"error": "object and recordId are required"}}, nil
	}

	limit := 100
	if s := firstQueryValue(req, "limit"); s != "" {
		if n, err := strconv.Atoi(s); err == nil && n > 0 {
			limit = n
		}
	}
	if limit > 500 {
		limit = 500
	}

	offset := 0
	if s := firstQueryValue(req, "offset"); s != "" {
		if n, err := strconv.Atoi(s); err == nil && n >= 0 {
			offset = n
		}
	}

	result, err := auditLog.QueryAuditLog(ctx, contract.AuditLogQueryRequest{
		ObjectAPIName: object,
		RecordID:      recordID,
		Limit:         limit,
		Offset:        offset,
	}, req.User)
	if err != nil {
		return adminAuditLogErrorResponse(err), nil
	}
	if result == nil {
		result = &contract.AuditLogQueryResult{Limit: limit, Offset: offset, Object: object}
	}

	rows := make([]adminAuditLogRow, 0, len(result.Rows))
	for _, row := range result.Rows {
		rows = append(rows, adminAuditLogRow{
			ID:               row.ID,
			ObjectAPIName:    row.ObjectAPIName,
			RecordID:         row.RecordID,
			FieldName:        row.FieldName,
			OldValue:         row.OldValue,
			NewValue:         row.NewValue,
			ChangedByID:      row.ChangedByID,
			ChangedAt:        auditLogChangedAt(row),
			Action:           stringValue(row.Action),
			ChangesJSON:      stringValue(row.ChangesJSON),
			Entrypoint:       stringValue(row.Entrypoint),
			EntrypointDetail: stringValue(row.EntrypointDetail),
			RequestID:        stringValue(row.RequestID),
			TraceID:          stringValue(row.TraceID),
			SessionID:        stringValue(row.SessionID),
			ClientIP:         stringValue(row.ClientIP),
			ActualUserID:     stringValue(row.ActualUserID),
			Outcome:          stringValue(row.Outcome),
			ErrorCode:        stringValue(row.ErrorCode),
		})
	}

	return &dialect.Response{Status: http.StatusOK, Body: adminAuditLogResponse{
		Records: rows,
		Count:   len(rows),
		Limit:   result.Limit,
		Offset:  result.Offset,
		Object:  result.Object,
	}}, nil
}

func adminAuditLogErrorResponse(err error) *dialect.Response {
	var auditErr *contract.AuditLogError
	if errors.As(err, &auditErr) {
		switch auditErr.Kind {
		case contract.AuditLogErrorUnavailable:
			return &dialect.Response{Status: http.StatusServiceUnavailable, Body: map[string]any{"error": "audit log backend not initialized"}}
		case contract.AuditLogErrorScanFailed:
			return &dialect.Response{Status: http.StatusInternalServerError, Body: map[string]any{"error": "audit log scan failed"}}
		case contract.AuditLogErrorIterate:
			return &dialect.Response{Status: http.StatusInternalServerError, Body: map[string]any{"error": "audit log iterate failed"}}
		default:
			return &dialect.Response{Status: http.StatusInternalServerError, Body: map[string]any{"error": "audit log query failed"}}
		}
	}
	return &dialect.Response{Status: http.StatusInternalServerError, Body: map[string]any{"error": "audit log query failed"}}
}

func auditLogChangedAt(row contract.AuditLogRow) string {
	if !row.ChangedAtValid {
		return ""
	}
	return row.ChangedAt.UTC().Format(time.RFC3339Nano)
}

func stringValue(v *string) string {
	if v == nil {
		return ""
	}
	return *v
}
