package repository

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"strings"
	v1 "template-plus/api/v1"
	"template-plus/internal/model"
	"template-plus/pkg/sid"

	"github.com/spf13/viper"
)

type AuditLogRepository interface {
	InsertButton(ctx context.Context, auditLog *model.AuditLog) (sql.Result, error)
	SelectButtonPage(ctx context.Context, pageInfo *v1.AutidLogPageInfo) ([]model.AuditLog, int64, error)
}

type auditLogRepository struct {
	generic GenericRepository[model.AuditLog]
}

func NewAuditLogRepository(r *Repository, v *viper.Viper, s *sid.Sid) AuditLogRepository {
	return &auditLogRepository{generic: NewGenericRepository[model.AuditLog](r, v, s)}
}

const (
	selectAuditLogsPage = `
			SELECT 
					sal.id,
					sal.action,
					sal.target_id,
					sal.target_type,
					sal.value,
					sal.created_at,
					COALESCE(creator.username, 'Unknown') AS creator
				FROM sys_audit_log sal
				LEFT JOIN sys_user creator 
					ON sal.created_by = creator.id`

	countAuditLogSQL = "SELECT COUNT(*) from sys_audit_log"

	insertAuditLogSQL = `
	INSERT INTO sys_button (
			id, action, target_id, target_type, value, 
			created_at, created_by
		) VALUES (
			:id, :action, :target_id, :target_type, :value,
			:created_at, :created_by
		)`
)

// InsertButton implements AuditLogRepository.
func (a *auditLogRepository) InsertButton(ctx context.Context, auditLog *model.AuditLog) (sql.Result, error) {
	if auditLog == nil {
		return nil, errors.New("auditLog cannot be nil")
	}

	return a.generic.Insert(ctx, insertAuditLogSQL, auditLog)
}

// SelectButtonPage implements AuditLogRepository.
func (a *auditLogRepository) SelectButtonPage(ctx context.Context, pageInfo *v1.AutidLogPageInfo) ([]model.AuditLog, int64, error) {
	if pageInfo == nil {
		return nil, 0, errors.New("page info cannot be null")
	}

	var (
		total  int64
		err    error
		query  strings.Builder
		params = make(map[string]any)
		order  = "DESC"
	)

	if pageInfo.Asc {
		order = "ASC"
	}

	query.WriteString(selectDictDataPageSQL)

	// Get total count of dict_types.
	if pageInfo.Username != "" {
		query.WriteString(" WHERE creator.username LIKE :username")
		params["username"] = pageInfo.Username + "%"
		total, err = a.generic.Count(ctx, countAuditLogSQL+" WHERE username LIKE $1", pageInfo.Username+"%")
		if pageInfo.TargetType != "" {
			query.WriteString(" AND sal.target_type = :target_type")
			params["target_type"] = pageInfo.TargetType
			total, err = a.generic.Count(ctx, countAuditLogSQL+" WHERE username LIKE $1 AND target_type = $2", pageInfo.Username+"%", pageInfo.TargetType)
		}
	} else if pageInfo.TargetType != "" {
		query.WriteString(" WHERE sal.target_type = :target_type")
		params["target_type"] = pageInfo.TargetType
		total, err = a.generic.Count(ctx, countAuditLogSQL+" WHERE target_type = $1", pageInfo.TargetType)
	} else {
		total, err = a.generic.Count(ctx, countDictDataSQL)
	}

	if err != nil {
		return nil, 0, err
	}

	// Append order, limit and offset for pagination.
	query.WriteString(fmt.Sprintf(" ORDER BY %s %s LIMIT :size OFFSET :offset", pageInfo.Sort, order))
	params["size"] = pageInfo.Size
	params["offset"] = pageInfo.Offset

	// Fetch paginated results.
	dictDatas, err := a.generic.SelectNamed(ctx, query.String(), params)
	if err != nil {
		return nil, 0, err
	}

	return dictDatas, total, nil
}
please optimize the code and add comments