package handlers

import (
	"api-service/internal/config"
	"api-service/internal/database"
	models "api-service/internal/models"
	"api-service/internal/models/retribusi"
	utils "api-service/internal/utils/filters"
	"api-service/internal/utils/validation"
	"api-service/pkg/logger"
	"context"
	"database/sql"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/google/uuid"
)

var (
	db       database.Service
	once     sync.Once
	validate *validator.Validate
)

// Initialize the database connection and validator
func init() {
	once.Do(func() {
		db = database.New(config.LoadConfig())
		validate = validator.New()

		// Register custom validations if needed
		validate.RegisterValidation("retribusi_status", validateRetribusiStatus)

		if db == nil {
			logger.Fatal("Failed to initialize database connection")
		}
	})
}

// Custom validation for retribusi status
func validateRetribusiStatus(fl validator.FieldLevel) bool {
	return models.IsValidStatus(fl.Field().String())
}

// RetribusiHandler handles retribusi services
type RetribusiHandler struct {
	db database.Service
}

// NewRetribusiHandler creates a new RetribusiHandler
func NewRetribusiHandler() *RetribusiHandler {
	return &RetribusiHandler{
		db: db,
	}
}

// GetRetribusi godoc
// @Summary Get retribusi with pagination and optional aggregation
// @Description Returns a paginated list of retribusis with optional summary statistics
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param limit query int false "Limit (max 100)" default(10)
// @Param offset query int false "Offset" default(0)
// @Param include_summary query bool false "Include aggregation summary" default(false)
// @Param status query string false "Filter by status"
// @Param jenis query string false "Filter by jenis"
// @Param dinas query string false "Filter by dinas"
// @Param search query string false "Search in multiple fields"
// @Success 200 {object} retribusi.RetribusiGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis [get]
func (h *RetribusiHandler) GetRetribusi(c *gin.Context) {
	// Parse pagination parameters
	limit, offset, err := h.parsePaginationParams(c)
	if err != nil {
		h.respondError(c, "Invalid pagination parameters", err, http.StatusBadRequest)
		return
	}

	// Parse filter parameters
	filter := h.parseFilterParams(c)
	includeAggregation := c.Query("include_summary") == "true"

	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	// Create context with timeout
	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute concurrent operations
	var (
		retribusis    []retribusi.Retribusi
		total         int
		aggregateData *models.AggregateData
		wg            sync.WaitGroup
		errChan       = make(chan error, 3)
		mu            sync.Mutex
	)

	// Fetch total count
	wg.Add(1)
	go func() {
		defer wg.Done()
		if err := h.getTotalCount(ctx, dbConn, filter, &total); err != nil {
			mu.Lock()
			errChan <- fmt.Errorf("failed to get total count: %w", err)
			mu.Unlock()
		}
	}()

	// Fetch main data
	wg.Add(1)
	go func() {
		defer wg.Done()
		result, err := h.fetchRetribusis(ctx, dbConn, filter, limit, offset)
		mu.Lock()
		if err != nil {
			errChan <- fmt.Errorf("failed to fetch data: %w", err)
		} else {
			retribusis = result
		}
		mu.Unlock()
	}()

	// Fetch aggregation data if requested
	if includeAggregation {
		wg.Add(1)
		go func() {
			defer wg.Done()
			result, err := h.getAggregateData(ctx, dbConn, filter)
			mu.Lock()
			if err != nil {
				errChan <- fmt.Errorf("failed to get aggregate data: %w", err)
			} else {
				aggregateData = result
			}
			mu.Unlock()
		}()
	}

	// Wait for all goroutines
	wg.Wait()
	close(errChan)

	// Check for errors
	for err := range errChan {
		if err != nil {
			h.logAndRespondError(c, "Data processing failed", err, http.StatusInternalServerError)
			return
		}
	}

	// Build response
	meta := h.calculateMeta(limit, offset, total)
	response := retribusi.RetribusiGetResponse{
		Message: "Data retribusi berhasil diambil",
		Data:    retribusis,
		Meta:    meta,
	}

	if includeAggregation && aggregateData != nil {
		response.Summary = aggregateData
	}

	c.JSON(http.StatusOK, response)
}

// GetRetribusiByID godoc
// @Summary Get Retribusi by ID
// @Description Returns a single retribusi by ID
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param id path string true "Retribusi ID (UUID)"
// @Success 200 {object} retribusi.RetribusiGetByIDResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [get]
func (h *RetribusiHandler) GetRetribusiByID(c *gin.Context) {
	id := c.Param("id")

	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	dataretribusi, err := h.getRetribusiByID(ctx, dbConn, id)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "Retribusi not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to get retribusi", err, http.StatusInternalServerError)
		}
		return
	}

	response := retribusi.RetribusiGetByIDResponse{
		Message: "Retribusi details retrieved successfully",
		Data:    dataretribusi,
	}

	c.JSON(http.StatusOK, response)
}

// GetRetribusiDynamic godoc
// @Summary Get retribusi with dynamic filtering
// @Description Returns retribusis with advanced dynamic filtering like Directus
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param fields query string false "Fields to select (e.g., fields=*.*)"
// @Param filter[column][operator] query string false "Dynamic filters (e.g., filter[Jenis][_eq]=value)"
// @Param sort query string false "Sort fields (e.g., sort=date_created,-Jenis)"
// @Param limit query int false "Limit" default(10)
// @Param offset query int false "Offset" default(0)
// @Success 200 {object} retribusi.RetribusiGetResponse "Success response"
// @Failure 400 {object} models.ErrorResponse "Bad request"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis/dynamic [get]
func (h *RetribusiHandler) GetRetribusiDynamic(c *gin.Context) {
	// Parse query parameters
	parser := utils.NewQueryParser().SetLimits(10, 100)
	dynamicQuery, err := parser.ParseQuery(c.Request.URL.Query())
	if err != nil {
		h.respondError(c, "Invalid query parameters", err, http.StatusBadRequest)
		return
	}

	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	// Create context with timeout
	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute query with dynamic filtering
	retribusis, total, err := h.fetchRetribusisDynamic(ctx, dbConn, dynamicQuery)
	if err != nil {
		h.logAndRespondError(c, "Failed to fetch data", err, http.StatusInternalServerError)
		return
	}

	// Build response
	meta := h.calculateMeta(dynamicQuery.Limit, dynamicQuery.Offset, total)
	response := retribusi.RetribusiGetResponse{
		Message: "Data retribusi berhasil diambil",
		Data:    retribusis,
		Meta:    meta,
	}

	c.JSON(http.StatusOK, response)
}

// fetchRetribusisDynamic executes dynamic query
func (h *RetribusiHandler) fetchRetribusisDynamic(ctx context.Context, dbConn *sql.DB, query utils.DynamicQuery) ([]retribusi.Retribusi, int, error) {
	// Setup query builder
	countBuilder := utils.NewQueryBuilder("data_retribusi").
		SetColumnMapping(map[string]string{
			"jenis":           "Jenis",
			"pelayanan":       "Pelayanan",
			"dinas":           "Dinas",
			"kelompok_obyek":  "Kelompok_obyek",
			"Kode_tarif":      "Kode_tarif",
			"kode_tarif":      "Kode_tarif",
			"tarif":           "Tarif",
			"satuan":          "Satuan",
			"tarif_overtime":  "Tarif_overtime",
			"satuan_overtime": "Satuan_overtime",
			"rekening_pokok":  "Rekening_pokok",
			"rekening_denda":  "Rekening_denda",
			"uraian_1":        "Uraian_1",
			"uraian_2":        "Uraian_2",
			"uraian_3":        "Uraian_3",
		}).
		SetAllowedColumns([]string{
			"id", "status", "sort", "user_created", "date_created",
			"user_updated", "date_updated", "Jenis", "Pelayanan",
			"Dinas", "Kelompok_obyek", "Kode_tarif", "Tarif", "Satuan",
			"Tarif_overtime", "Satuan_overtime", "Rekening_pokok",
			"Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3",
		})

	mainBuilder := utils.NewQueryBuilder("data_retribusi").
		SetColumnMapping(map[string]string{
			"jenis":           "Jenis",
			"pelayanan":       "Pelayanan",
			"dinas":           "Dinas",
			"kelompok_obyek":  "Kelompok_obyek",
			"Kode_tarif":      "Kode_tarif",
			"kode_tarif":      "Kode_tarif",
			"tarif":           "Tarif",
			"satuan":          "Satuan",
			"tarif_overtime":  "Tarif_overtime",
			"satuan_overtime": "Satuan_overtime",
			"rekening_pokok":  "Rekening_pokok",
			"rekening_denda":  "Rekening_denda",
			"uraian_1":        "Uraian_1",
			"uraian_2":        "Uraian_2",
			"uraian_3":        "Uraian_3",
		}).
		SetAllowedColumns([]string{
			"id", "status", "sort", "user_created", "date_created",
			"user_updated", "date_updated", "Jenis", "Pelayanan",
			"Dinas", "Kelompok_obyek", "Kode_tarif", "Tarif", "Satuan",
			"Tarif_overtime", "Satuan_overtime", "Rekening_pokok",
			"Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3",
		})

	// Add default filter to exclude deleted records
	if len(query.Filters) > 0 {
		query.Filters = append([]utils.FilterGroup{{
			Filters: []utils.DynamicFilter{{
				Column:   "status",
				Operator: utils.OpNotEqual,
				Value:    "deleted",
			}},
			LogicOp: "AND",
		}}, query.Filters...)
	} else {
		query.Filters = []utils.FilterGroup{{
			Filters: []utils.DynamicFilter{{
				Column:   "status",
				Operator: utils.OpNotEqual,
				Value:    "deleted",
			}},
			LogicOp: "AND",
		}}
	}

	// Execute queries sequentially to avoid race conditions
	var total int
	var retribusis []retribusi.Retribusi

	// 1. Get total count first
	countQuery := query
	countQuery.Limit = 0
	countQuery.Offset = 0

	countSQL, countArgs, err := countBuilder.BuildCountQuery(countQuery)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to build count query: %w", err)
	}

	if err := dbConn.QueryRowContext(ctx, countSQL, countArgs...).Scan(&total); err != nil {
		return nil, 0, fmt.Errorf("failed to get total count: %w", err)
	}

	// 2. Get main data
	mainSQL, mainArgs, err := mainBuilder.BuildQuery(query)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to build main query: %w", err)
	}

	rows, err := dbConn.QueryContext(ctx, mainSQL, mainArgs...)
	if err != nil {
		return nil, 0, fmt.Errorf("failed to execute main query: %w", err)
	}
	defer rows.Close()

	for rows.Next() {
		retribusi, err := h.scanRetribusi(rows)
		if err != nil {
			return nil, 0, fmt.Errorf("failed to scan retribusi: %w", err)
		}
		retribusis = append(retribusis, retribusi)
	}

	if err := rows.Err(); err != nil {
		return nil, 0, fmt.Errorf("rows iteration error: %w", err)
	}

	return retribusis, total, nil
}

// SearchRetribusiAdvanced provides advanced search capabilities
func (h *RetribusiHandler) SearchRetribusiAdvanced(c *gin.Context) {
	// Parse complex search parameters
	searchQuery := c.Query("q")
	if searchQuery == "" {
		// If no search query provided, return all records with default sorting
		query := utils.DynamicQuery{
			Fields:  []string{"*"},
			Filters: []utils.FilterGroup{}, // Empty filters - fetchRetribusisDynamic will add default deleted filter
			Sort: []utils.SortField{{
				Column: "date_created",
				Order:  "DESC",
			}},
			Limit:  20,
			Offset: 0,
		}

		// Parse pagination if provided
		if limit := c.Query("limit"); limit != "" {
			if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 {
				query.Limit = l
			}
		}

		if offset := c.Query("offset"); offset != "" {
			if o, err := strconv.Atoi(offset); err == nil && o >= 0 {
				query.Offset = o
			}
		}

		// Get database connection
		dbConn, err := h.db.GetDB("postgres_satudata")
		if err != nil {
			h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
			return
		}

		ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
		defer cancel()

		// Execute query to get all records
		retribusis, total, err := h.fetchRetribusisDynamic(ctx, dbConn, query)
		if err != nil {
			h.logAndRespondError(c, "Failed to fetch data", err, http.StatusInternalServerError)
			return
		}

		// Build response
		meta := h.calculateMeta(query.Limit, query.Offset, total)
		response := retribusi.RetribusiGetResponse{
			Message: "All records retrieved (no search query provided)",
			Data:    retribusis,
			Meta:    meta,
		}

		c.JSON(http.StatusOK, response)
		return
	}

	// Build dynamic query for search
	query := utils.DynamicQuery{
		Fields: []string{"*"},
		Filters: []utils.FilterGroup{{
			Filters: []utils.DynamicFilter{
				{
					Column:   "Jenis",
					Operator: utils.OpContains,
					Value:    searchQuery,
					LogicOp:  "OR",
				},
				{
					Column:   "Pelayanan",
					Operator: utils.OpContains,
					Value:    searchQuery,
					LogicOp:  "OR",
				},
				{
					Column:   "Dinas",
					Operator: utils.OpContains,
					Value:    searchQuery,
					LogicOp:  "OR",
				},
				{
					Column:   "Uraian_1",
					Operator: utils.OpContains,
					Value:    searchQuery,
					LogicOp:  "OR",
				},
			},
			LogicOp: "AND",
		}},
		Sort: []utils.SortField{{
			Column: "date_created",
			Order:  "DESC",
		}},
		Limit:  20,
		Offset: 0,
	}

	// Parse pagination if provided
	if limit := c.Query("limit"); limit != "" {
		if l, err := strconv.Atoi(limit); err == nil && l > 0 && l <= 100 {
			query.Limit = l
		}
	}

	if offset := c.Query("offset"); offset != "" {
		if o, err := strconv.Atoi(offset); err == nil && o >= 0 {
			query.Offset = o
		}
	}

	// Get database connection
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 30*time.Second)
	defer cancel()

	// Execute search
	retribusis, total, err := h.fetchRetribusisDynamic(ctx, dbConn, query)
	if err != nil {
		h.logAndRespondError(c, "Search failed", err, http.StatusInternalServerError)
		return
	}

	// Build response
	meta := h.calculateMeta(query.Limit, query.Offset, total)
	response := retribusi.RetribusiGetResponse{
		Message: fmt.Sprintf("Search results for '%s'", searchQuery),
		Data:    retribusis,
		Meta:    meta,
	}

	c.JSON(http.StatusOK, response)
}

// CreateRetribusi godoc
// @Summary Create retribusi
// @Description Creates a new retribusi record
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param request body retribusi.RetribusiCreateRequest true "Retribusi creation request"
// @Success 201 {object} retribusi.RetribusiCreateResponse "Retribusi created successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis [post]
func (h *RetribusiHandler) CreateRetribusi(c *gin.Context) {
	var req retribusi.RetribusiCreateRequest

	if err := c.ShouldBindJSON(&req); err != nil {
		h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
		return
	}

	// Validate request
	if err := validate.Struct(&req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	// Validate duplicate and daily submission
	if err := h.validateRetribusiSubmission(ctx, dbConn, &req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	dataretribusi, err := h.createRetribusi(ctx, dbConn, &req)
	if err != nil {
		h.logAndRespondError(c, "Failed to create retribusi", err, http.StatusInternalServerError)
		return
	}

	response := retribusi.RetribusiCreateResponse{
		Message: "Retribusi berhasil dibuat",
		Data:    dataretribusi,
	}

	c.JSON(http.StatusCreated, response)
}

// UpdateRetribusi godoc
// @Summary Update retribusi
// @Description Updates an existing retribusi record
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param id path string true "Retribusi ID (UUID)"
// @Param request body retribusi.RetribusiUpdateRequest true "Retribusi update request"
// @Success 200 {object} retribusi.RetribusiUpdateResponse "Retribusi updated successfully"
// @Failure 400 {object} models.ErrorResponse "Bad request or validation error"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [put]
func (h *RetribusiHandler) UpdateRetribusi(c *gin.Context) {
	id := c.Param("id")

	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	var req retribusi.RetribusiUpdateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
		return
	}

	// Set ID from path parameter
	req.ID = id

	// Validate request
	if err := validate.Struct(&req); err != nil {
		h.respondError(c, "Validation failed", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	dataretribusi, err := h.updateRetribusi(ctx, dbConn, &req)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "Retribusi not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to update retribusi", err, http.StatusInternalServerError)
		}
		return
	}

	response := retribusi.RetribusiUpdateResponse{
		Message: "Retribusi berhasil diperbarui",
		Data:    dataretribusi,
	}

	c.JSON(http.StatusOK, response)
}

// DeleteRetribusi godoc
// @Summary Delete retribusi
// @Description Soft deletes a retribusi by setting status to 'deleted'
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param id path string true "Retribusi ID (UUID)"
// @Success 200 {object} retribusi.RetribusiDeleteResponse "Retribusi deleted successfully"
// @Failure 400 {object} models.ErrorResponse "Invalid ID format"
// @Failure 404 {object} models.ErrorResponse "Retribusi not found"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusi/{id} [delete]
func (h *RetribusiHandler) DeleteRetribusi(c *gin.Context) {
	id := c.Param("id")

	// Validate UUID format
	if _, err := uuid.Parse(id); err != nil {
		h.respondError(c, "Invalid ID format", err, http.StatusBadRequest)
		return
	}

	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	err = h.deleteRetribusi(ctx, dbConn, id)
	if err != nil {
		if err == sql.ErrNoRows {
			h.respondError(c, "Retribusi not found", err, http.StatusNotFound)
		} else {
			h.logAndRespondError(c, "Failed to delete retribusi", err, http.StatusInternalServerError)
		}
		return
	}

	response := retribusi.RetribusiDeleteResponse{
		Message: "Retribusi berhasil dihapus",
		ID:      id,
	}

	c.JSON(http.StatusOK, response)
}

// GetRetribusiStats godoc
// @Summary Get retribusi statistics
// @Description Returns comprehensive statistics about retribusi data
// @Tags Retribusi
// @Accept json
// @Produce json
// @Param status query string false "Filter statistics by status"
// @Success 200 {object} models.AggregateData "Statistics data"
// @Failure 500 {object} models.ErrorResponse "Internal server error"
// @Router /api/v1/retribusis/stats [get]
func (h *RetribusiHandler) GetRetribusiStats(c *gin.Context) {
	dbConn, err := h.db.GetDB("postgres_satudata")
	if err != nil {
		h.logAndRespondError(c, "Database connection failed", err, http.StatusInternalServerError)
		return
	}

	ctx, cancel := context.WithTimeout(c.Request.Context(), 15*time.Second)
	defer cancel()

	filter := h.parseFilterParams(c)
	aggregateData, err := h.getAggregateData(ctx, dbConn, filter)
	if err != nil {
		h.logAndRespondError(c, "Failed to get statistics", err, http.StatusInternalServerError)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "Statistik retribusi berhasil diambil",
		"data":    aggregateData,
	})
}

// Get retribusi by ID
func (h *RetribusiHandler) getRetribusiByID(ctx context.Context, dbConn *sql.DB, id string) (*retribusi.Retribusi, error) {
	query := `
        SELECT 
            id, status, sort, user_created, date_created, user_updated, date_updated,
            "Jenis", "Pelayanan", "Dinas", "Kelompok_obyek", "Kode_tarif", 
            "Tarif", "Satuan", "Tarif_overtime", "Satuan_overtime", 
            "Rekening_pokok", "Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3"
        FROM data_retribusi 
        WHERE id = $1 AND status != 'deleted'`

	row := dbConn.QueryRowContext(ctx, query, id)

	var retribusi retribusi.Retribusi
	err := row.Scan(
		&retribusi.ID, &retribusi.Status, &retribusi.Sort, &retribusi.UserCreated,
		&retribusi.DateCreated, &retribusi.UserUpdated, &retribusi.DateUpdated,
		&retribusi.Jenis, &retribusi.Pelayanan, &retribusi.Dinas, &retribusi.KelompokObyek,
		&retribusi.KodeTarif, &retribusi.Tarif, &retribusi.Satuan, &retribusi.TarifOvertime,
		&retribusi.SatuanOvertime, &retribusi.RekeningPokok, &retribusi.RekeningDenda,
		&retribusi.Uraian1, &retribusi.Uraian2, &retribusi.Uraian3,
	)

	if err != nil {
		return nil, err
	}

	return &retribusi, nil
}

// Create retribusi
func (h *RetribusiHandler) createRetribusi(ctx context.Context, dbConn *sql.DB, req *retribusi.RetribusiCreateRequest) (*retribusi.Retribusi, error) {
	id := uuid.New().String()
	now := time.Now()

	query := `
        INSERT INTO data_retribusi (
            id, status, date_created, date_updated,
            "Jenis", "Pelayanan", "Dinas", "Kelompok_obyek", "Kode_tarif", 
            "Tarif", "Satuan", "Tarif_overtime", "Satuan_overtime", 
            "Rekening_pokok", "Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3"
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18)
        RETURNING 
            id, status, sort, user_created, date_created, user_updated, date_updated,
            "Jenis", "Pelayanan", "Dinas", "Kelompok_obyek", "Kode_tarif", 
            "Tarif", "Satuan", "Tarif_overtime", "Satuan_overtime", 
            "Rekening_pokok", "Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3"`

	row := dbConn.QueryRowContext(ctx, query,
		id, req.Status, now, now,
		req.Jenis, req.Pelayanan, req.Dinas, req.KelompokObyek, req.KodeTarif,
		req.Tarif, req.Satuan, req.TarifOvertime, req.SatuanOvertime,
		req.RekeningPokok, req.RekeningDenda, req.Uraian1, req.Uraian2, req.Uraian3,
	)

	var retribusi retribusi.Retribusi
	err := row.Scan(
		&retribusi.ID, &retribusi.Status, &retribusi.Sort, &retribusi.UserCreated,
		&retribusi.DateCreated, &retribusi.UserUpdated, &retribusi.DateUpdated,
		&retribusi.Jenis, &retribusi.Pelayanan, &retribusi.Dinas, &retribusi.KelompokObyek,
		&retribusi.KodeTarif, &retribusi.Tarif, &retribusi.Satuan, &retribusi.TarifOvertime,
		&retribusi.SatuanOvertime, &retribusi.RekeningPokok, &retribusi.RekeningDenda,
		&retribusi.Uraian1, &retribusi.Uraian2, &retribusi.Uraian3,
	)

	if err != nil {
		return nil, fmt.Errorf("failed to create retribusi: %w", err)
	}

	return &retribusi, nil
}

// Update retribusi
func (h *RetribusiHandler) updateRetribusi(ctx context.Context, dbConn *sql.DB, req *retribusi.RetribusiUpdateRequest) (*retribusi.Retribusi, error) {
	now := time.Now()

	query := `
        UPDATE data_retribusi SET 
            status = $2, date_updated = $3,
            "Jenis" = $4, "Pelayanan" = $5, "Dinas" = $6, "Kelompok_obyek" = $7, "Kode_tarif" = $8,
            "Tarif" = $9, "Satuan" = $10, "Tarif_overtime" = $11, "Satuan_overtime" = $12,
            "Rekening_pokok" = $13, "Rekening_denda" = $14, "Uraian_1" = $15, "Uraian_2" = $16, "Uraian_3" = $17
        WHERE id = $1 AND status != 'deleted'
        RETURNING 
            id, status, sort, user_created, date_created, user_updated, date_updated,
            "Jenis", "Pelayanan", "Dinas", "Kelompok_obyek", "Kode_tarif", 
            "Tarif", "Satuan", "Tarif_overtime", "Satuan_overtime", 
            "Rekening_pokok", "Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3"`

	row := dbConn.QueryRowContext(ctx, query,
		req.ID, req.Status, now,
		req.Jenis, req.Pelayanan, req.Dinas, req.KelompokObyek, req.KodeTarif,
		req.Tarif, req.Satuan, req.TarifOvertime, req.SatuanOvertime,
		req.RekeningPokok, req.RekeningDenda, req.Uraian1, req.Uraian2, req.Uraian3,
	)

	var retribusi retribusi.Retribusi
	err := row.Scan(
		&retribusi.ID, &retribusi.Status, &retribusi.Sort, &retribusi.UserCreated,
		&retribusi.DateCreated, &retribusi.UserUpdated, &retribusi.DateUpdated,
		&retribusi.Jenis, &retribusi.Pelayanan, &retribusi.Dinas, &retribusi.KelompokObyek,
		&retribusi.KodeTarif, &retribusi.Tarif, &retribusi.Satuan, &retribusi.TarifOvertime,
		&retribusi.SatuanOvertime, &retribusi.RekeningPokok, &retribusi.RekeningDenda,
		&retribusi.Uraian1, &retribusi.Uraian2, &retribusi.Uraian3,
	)

	if err != nil {
		return nil, fmt.Errorf("failed to update retribusi: %w", err)
	}

	return &retribusi, nil
}

// Soft delete retribusi
func (h *RetribusiHandler) deleteRetribusi(ctx context.Context, dbConn *sql.DB, id string) error {
	now := time.Now()

	query := `UPDATE data_retribusi SET status = 'deleted', date_updated = $2 WHERE id = $1 AND status != 'deleted'`

	result, err := dbConn.ExecContext(ctx, query, id, now)
	if err != nil {
		return fmt.Errorf("failed to delete retribusi: %w", err)
	}

	rowsAffected, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("failed to get affected rows: %w", err)
	}

	if rowsAffected == 0 {
		return sql.ErrNoRows
	}

	return nil
}

// Enhanced error handling
func (h *RetribusiHandler) logAndRespondError(c *gin.Context, message string, err error, statusCode int) {
	logger.Error(message, map[string]interface{}{
		"error":       err.Error(),
		"status_code": statusCode,
	})
	h.respondError(c, message, err, statusCode)
}

func (h *RetribusiHandler) respondError(c *gin.Context, message string, err error, statusCode int) {
	errorMessage := message
	if gin.Mode() == gin.ReleaseMode {
		errorMessage = "Internal server error"
	}

	c.JSON(statusCode, models.ErrorResponse{
		Error:     errorMessage,
		Code:      statusCode,
		Message:   err.Error(),
		Timestamp: time.Now(),
	})
}

// Parse pagination parameters dengan validation yang lebih ketat
func (h *RetribusiHandler) parsePaginationParams(c *gin.Context) (int, int, error) {
	limit := 10 // Default limit
	offset := 0 // Default offset

	if limitStr := c.Query("limit"); limitStr != "" {
		parsedLimit, err := strconv.Atoi(limitStr)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid limit parameter: %s", limitStr)
		}
		if parsedLimit <= 0 {
			return 0, 0, fmt.Errorf("limit must be greater than 0")
		}
		if parsedLimit > 100 {
			return 0, 0, fmt.Errorf("limit cannot exceed 100")
		}
		limit = parsedLimit
	}

	if offsetStr := c.Query("offset"); offsetStr != "" {
		parsedOffset, err := strconv.Atoi(offsetStr)
		if err != nil {
			return 0, 0, fmt.Errorf("invalid offset parameter: %s", offsetStr)
		}
		if parsedOffset < 0 {
			return 0, 0, fmt.Errorf("offset cannot be negative")
		}
		offset = parsedOffset
	}

	logger.Debug("Pagination parameters", map[string]interface{}{
		"limit":  limit,
		"offset": offset,
	})
	return limit, offset, nil
}

// Build WHERE clause dengan filter parameters
func (h *RetribusiHandler) buildWhereClause(filter retribusi.RetribusiFilter) (string, []interface{}) {
	conditions := []string{"status != 'deleted'"}
	args := []interface{}{}
	paramCount := 1

	if filter.Status != nil {
		conditions = append(conditions, fmt.Sprintf("status = $%d", paramCount))
		args = append(args, *filter.Status)
		paramCount++
	}

	if filter.Jenis != nil {
		conditions = append(conditions, fmt.Sprintf(`"Jenis" ILIKE $%d`, paramCount))
		args = append(args, "%"+*filter.Jenis+"%")
		paramCount++
	}

	if filter.Dinas != nil {
		conditions = append(conditions, fmt.Sprintf(`"Dinas" ILIKE $%d`, paramCount))
		args = append(args, "%"+*filter.Dinas+"%")
		paramCount++
	}

	if filter.KelompokObyek != nil {
		conditions = append(conditions, fmt.Sprintf(`"Kelompok_obyek" ILIKE $%d`, paramCount))
		args = append(args, "%"+*filter.KelompokObyek+"%")
		paramCount++
	}

	if filter.Search != nil {
		searchCondition := fmt.Sprintf(`(
            "Jenis" ILIKE $%d OR 
            "Pelayanan" ILIKE $%d OR 
            "Dinas" ILIKE $%d OR 
			"Kode_tarif" ILIKE $%d OR
            "Uraian_1" ILIKE $%d OR 
            "Uraian_2" ILIKE $%d OR 
            "Uraian_3" ILIKE $%d
        )`, paramCount, paramCount, paramCount, paramCount, paramCount, paramCount, paramCount)
		conditions = append(conditions, searchCondition)
		searchTerm := "%" + *filter.Search + "%"
		args = append(args, searchTerm)
		paramCount++
	}

	if filter.DateFrom != nil {
		conditions = append(conditions, fmt.Sprintf("date_created >= $%d", paramCount))
		args = append(args, *filter.DateFrom)
		paramCount++
	}

	if filter.DateTo != nil {
		conditions = append(conditions, fmt.Sprintf("date_created <= $%d", paramCount))
		args = append(args, filter.DateTo.Add(24*time.Hour-time.Nanosecond)) // End of day
		paramCount++
	}

	return strings.Join(conditions, " AND "), args
}

// Optimized scanning function yang menggunakan sql.Null* types langsung
func (h *RetribusiHandler) scanRetribusi(rows *sql.Rows) (retribusi.Retribusi, error) {
	var retribusi retribusi.Retribusi

	return retribusi, rows.Scan(
		&retribusi.ID,
		&retribusi.Status,
		&retribusi.Sort,
		&retribusi.UserCreated,
		&retribusi.DateCreated,
		&retribusi.UserUpdated,
		&retribusi.DateUpdated,
		&retribusi.Jenis,
		&retribusi.Pelayanan,
		&retribusi.Dinas,
		&retribusi.KelompokObyek,
		&retribusi.KodeTarif,
		&retribusi.Tarif,
		&retribusi.Satuan,
		&retribusi.TarifOvertime,
		&retribusi.SatuanOvertime,
		&retribusi.RekeningPokok,
		&retribusi.RekeningDenda,
		&retribusi.Uraian1,
		&retribusi.Uraian2,
		&retribusi.Uraian3,
	)
}

// Parse filter parameters dari query string
func (h *RetribusiHandler) parseFilterParams(c *gin.Context) retribusi.RetribusiFilter {
	filter := retribusi.RetribusiFilter{}

	if status := c.Query("status"); status != "" {
		if models.IsValidStatus(status) {
			filter.Status = &status
		}
	}

	if jenis := c.Query("jenis"); jenis != "" {
		filter.Jenis = &jenis
	}

	if dinas := c.Query("dinas"); dinas != "" {
		filter.Dinas = &dinas
	}

	if kelompokObyek := c.Query("kelompok_obyek"); kelompokObyek != "" {
		filter.KelompokObyek = &kelompokObyek
	}

	if search := c.Query("search"); search != "" {
		filter.Search = &search
	}

	// Parse date filters
	if dateFromStr := c.Query("date_from"); dateFromStr != "" {
		if dateFrom, err := time.Parse("2006-01-02", dateFromStr); err == nil {
			filter.DateFrom = &dateFrom
		}
	}

	if dateToStr := c.Query("date_to"); dateToStr != "" {
		if dateTo, err := time.Parse("2006-01-02", dateToStr); err == nil {
			filter.DateTo = &dateTo
		}
	}

	return filter
}

// Get comprehensive aggregate data dengan filter support
func (h *RetribusiHandler) getAggregateData(ctx context.Context, dbConn *sql.DB, filter retribusi.RetribusiFilter) (*models.AggregateData, error) {
	aggregate := &models.AggregateData{
		ByStatus: make(map[string]int),
		ByDinas:  make(map[string]int),
		ByJenis:  make(map[string]int),
	}

	// Build where clause untuk filter
	whereClause, args := h.buildWhereClause(filter)

	// Use concurrent execution untuk performance
	var wg sync.WaitGroup
	var mu sync.Mutex
	errChan := make(chan error, 4)

	// 1. Count by status
	wg.Add(1)
	go func() {
		defer wg.Done()
		statusQuery := fmt.Sprintf(`
            SELECT status, COUNT(*) 
            FROM data_retribusi 
            WHERE %s 
            GROUP BY status 
            ORDER BY status`, whereClause)

		rows, err := dbConn.QueryContext(ctx, statusQuery, args...)
		if err != nil {
			errChan <- fmt.Errorf("status query failed: %w", err)
			return
		}
		defer rows.Close()

		mu.Lock()
		for rows.Next() {
			var status string
			var count int
			if err := rows.Scan(&status, &count); err != nil {
				mu.Unlock()
				errChan <- fmt.Errorf("status scan failed: %w", err)
				return
			}
			aggregate.ByStatus[status] = count
			switch status {
			case "active":
				aggregate.TotalActive = count
			case "draft":
				aggregate.TotalDraft = count
			case "inactive":
				aggregate.TotalInactive = count
			}
		}
		mu.Unlock()

		if err := rows.Err(); err != nil {
			errChan <- fmt.Errorf("status iteration error: %w", err)
		}
	}()

	// 2. Count by Dinas
	wg.Add(1)
	go func() {
		defer wg.Done()
		dinasQuery := fmt.Sprintf(`
            SELECT COALESCE("Dinas", 'Unknown') as dinas, COUNT(*) 
            FROM data_retribusi 
            WHERE %s AND "Dinas" IS NOT NULL AND TRIM("Dinas") != ''
            GROUP BY "Dinas" 
            ORDER BY COUNT(*) DESC 
            LIMIT 10`, whereClause)

		rows, err := dbConn.QueryContext(ctx, dinasQuery, args...)
		if err != nil {
			errChan <- fmt.Errorf("dinas query failed: %w", err)
			return
		}
		defer rows.Close()

		mu.Lock()
		for rows.Next() {
			var dinas string
			var count int
			if err := rows.Scan(&dinas, &count); err != nil {
				mu.Unlock()
				errChan <- fmt.Errorf("dinas scan failed: %w", err)
				return
			}
			aggregate.ByDinas[dinas] = count
		}
		mu.Unlock()

		if err := rows.Err(); err != nil {
			errChan <- fmt.Errorf("dinas iteration error: %w", err)
		}
	}()

	// 3. Count by Jenis
	wg.Add(1)
	go func() {
		defer wg.Done()
		jenisQuery := fmt.Sprintf(`
            SELECT COALESCE("Jenis", 'Unknown') as jenis, COUNT(*) 
            FROM data_retribusi 
            WHERE %s AND "Jenis" IS NOT NULL AND TRIM("Jenis") != ''
            GROUP BY "Jenis" 
            ORDER BY COUNT(*) DESC 
            LIMIT 10`, whereClause)

		rows, err := dbConn.QueryContext(ctx, jenisQuery, args...)
		if err != nil {
			errChan <- fmt.Errorf("jenis query failed: %w", err)
			return
		}
		defer rows.Close()

		mu.Lock()
		for rows.Next() {
			var jenis string
			var count int
			if err := rows.Scan(&jenis, &count); err != nil {
				mu.Unlock()
				errChan <- fmt.Errorf("jenis scan failed: %w", err)
				return
			}
			aggregate.ByJenis[jenis] = count
		}
		mu.Unlock()

		if err := rows.Err(); err != nil {
			errChan <- fmt.Errorf("jenis iteration error: %w", err)
		}
	}()

	// 4. Get last updated time dan today statistics
	wg.Add(1)
	go func() {
		defer wg.Done()

		// Last updated
		lastUpdatedQuery := fmt.Sprintf(`
            SELECT MAX(date_updated) 
            FROM data_retribusi 
            WHERE %s AND date_updated IS NOT NULL`, whereClause)

		var lastUpdated sql.NullTime
		if err := dbConn.QueryRowContext(ctx, lastUpdatedQuery, args...).Scan(&lastUpdated); err != nil {
			errChan <- fmt.Errorf("last updated query failed: %w", err)
			return
		}

		// Today statistics
		today := time.Now().Format("2006-01-02")
		todayStatsQuery := fmt.Sprintf(`
            SELECT 
                SUM(CASE WHEN DATE(date_created) = $%d THEN 1 ELSE 0 END) as created_today,
                SUM(CASE WHEN DATE(date_updated) = $%d AND DATE(date_created) != $%d THEN 1 ELSE 0 END) as updated_today
            FROM data_retribusi 
            WHERE %s`, len(args)+1, len(args)+1, len(args)+1, whereClause)

		todayArgs := append(args, today)
		var createdToday, updatedToday int
		if err := dbConn.QueryRowContext(ctx, todayStatsQuery, todayArgs...).Scan(&createdToday, &updatedToday); err != nil {
			errChan <- fmt.Errorf("today stats query failed: %w", err)
			return
		}

		mu.Lock()
		if lastUpdated.Valid {
			aggregate.LastUpdated = &lastUpdated.Time
		}
		aggregate.CreatedToday = createdToday
		aggregate.UpdatedToday = updatedToday
		mu.Unlock()
	}()

	// Wait for all goroutines
	wg.Wait()
	close(errChan)

	// Check for errors
	for err := range errChan {
		if err != nil {
			return nil, err
		}
	}

	return aggregate, nil
}

// Get total count dengan filter support
func (h *RetribusiHandler) getTotalCount(ctx context.Context, dbConn *sql.DB, filter retribusi.RetribusiFilter, total *int) error {
	whereClause, args := h.buildWhereClause(filter)
	countQuery := fmt.Sprintf(`SELECT COUNT(*) FROM data_retribusi WHERE %s`, whereClause)

	if err := dbConn.QueryRowContext(ctx, countQuery, args...).Scan(total); err != nil {
		return fmt.Errorf("total count query failed: %w", err)
	}

	return nil
}

// Enhanced fetchRetribusis dengan filter support
func (h *RetribusiHandler) fetchRetribusis(ctx context.Context, dbConn *sql.DB, filter retribusi.RetribusiFilter, limit, offset int) ([]retribusi.Retribusi, error) {
	whereClause, args := h.buildWhereClause(filter)

	// Build the main query with pagination
	query := fmt.Sprintf(`
        SELECT 
            id, status, sort, user_created, date_created, user_updated, date_updated,
            "Jenis", "Pelayanan", "Dinas", "Kelompok_obyek", "Kode_tarif",
            "Tarif", "Satuan", "Tarif_overtime", "Satuan_overtime",
            "Rekening_pokok", "Rekening_denda", "Uraian_1", "Uraian_2", "Uraian_3"
        FROM data_retribusi 
        WHERE %s
        ORDER BY date_created DESC NULLS LAST
        LIMIT $%d OFFSET $%d`,
		whereClause, len(args)+1, len(args)+2)

	// Add pagination parameters
	args = append(args, limit, offset)

	rows, err := dbConn.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("fetch retribusis query failed: %w", err)
	}
	defer rows.Close()

	// Pre-allocate slice dengan kapasitas yang tepat
	retribusis := make([]retribusi.Retribusi, 0, limit)

	for rows.Next() {
		retribusi, err := h.scanRetribusi(rows)
		if err != nil {
			return nil, fmt.Errorf("scan retribusi failed: %w", err)
		}
		retribusis = append(retribusis, retribusi)
	}

	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("rows iteration error: %w", err)
	}

	logger.Info("Successfully fetched retribusis", map[string]interface{}{
		"count":  len(retribusis),
		"limit":  limit,
		"offset": offset,
	})
	return retribusis, nil
}

// Calculate pagination metadata
func (h *RetribusiHandler) calculateMeta(limit, offset, total int) models.MetaResponse {
	totalPages := 0
	currentPage := 1

	if limit > 0 {
		totalPages = (total + limit - 1) / limit // Ceiling division
		currentPage = (offset / limit) + 1
	}

	return models.MetaResponse{
		Limit:       limit,
		Offset:      offset,
		Total:       total,
		TotalPages:  totalPages,
		CurrentPage: currentPage,
		HasNext:     offset+limit < total,
		HasPrev:     offset > 0,
	}
}

// validateRetribusiSubmission performs validation for duplicate entries and daily submission limits
func (h *RetribusiHandler) validateRetribusiSubmission(ctx context.Context, dbConn *sql.DB, req *retribusi.RetribusiCreateRequest) error {
	// Import the validation utility
	validator := validation.NewDuplicateValidator(dbConn)

	// Use default retribusi configuration
	config := validation.DefaultRetribusiConfig()

	// Validate duplicate entries with active status for today
	err := validator.ValidateDuplicate(ctx, config, "dummy_id")
	if err != nil {
		return fmt.Errorf("validation failed: %w", err)
	}

	// Validate once per day submission
	err = validator.ValidateOncePerDay(ctx, "data_retribusi", "id", "date_created", "daily_limit")
	if err != nil {
		return fmt.Errorf("daily submission limit exceeded: %w", err)
	}

	return nil
}

// Example usage of the validation utility with custom configuration
func (h *RetribusiHandler) validateWithCustomConfig(ctx context.Context, dbConn *sql.DB, req *retribusi.RetribusiCreateRequest) error {
	// Create validator instance
	validator := validation.NewDuplicateValidator(dbConn)

	// Use custom configuration
	config := validation.ValidationConfig{
		TableName:      "data_retribusi",
		IDColumn:       "id",
		StatusColumn:   "status",
		DateColumn:     "date_created",
		ActiveStatuses: []string{"active", "draft"},
		AdditionalFields: map[string]interface{}{
			"jenis": req.Jenis,
			"dinas": req.Dinas,
		},
	}

	// Validate with custom fields
	fields := map[string]interface{}{
		"jenis": *req.Jenis,
		"dinas": *req.Dinas,
	}

	err := validator.ValidateDuplicateWithCustomFields(ctx, config, fields)
	if err != nil {
		return fmt.Errorf("custom validation failed: %w", err)
	}

	return nil
}

// GetLastSubmissionTime example
func (h *RetribusiHandler) getLastSubmissionTimeExample(ctx context.Context, dbConn *sql.DB, identifier string) (*time.Time, error) {
	validator := validation.NewDuplicateValidator(dbConn)
	return validator.GetLastSubmissionTime(ctx, "data_retribusi", "id", "date_created", identifier)
}
