package services

import (
	"bookhoard/internal/database"
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5/pgtype"
)

// ErrFilterNotFound - Custom errors for saved filters
var ErrFilterNotFound = errors.New("filter not found or access denied")

type FiltersService struct {
	db *database.Queries
}

func NewFiltersService(db *database.Queries) *FiltersService {
	return &FiltersService{db: db}
}

// GetSavedFilters - Retrieve all saved filters for a user + resource type
func (s *FiltersService) GetSavedFilters(ctx context.Context, userID uuid.UUID, resourceType string) ([]database.SavedFilters, error) {
	filters, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{
		UserID:       pgtype.UUID{Bytes: userID, Valid: true},
		ResourceType: resourceType,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to get saved filters: %w", err)
	}

	return filters, nil
}

// GetSavedFilterByID - Retrieve a single saved filter by ID
func (s *FiltersService) GetSavedFilterByID(ctx context.Context, userID uuid.UUID, filterID uuid.UUID) (database.SavedFilters, error) {
	filter, err := s.db.GetSavedFilterByID(ctx, database.GetSavedFilterByIDParams{
		ID:     pgtype.UUID{Bytes: filterID, Valid: true},
		UserID: pgtype.UUID{Bytes: userID, Valid: true},
	})
	if err != nil {
		return database.SavedFilters{}, ErrFilterNotFound
	}

	return filter, nil
}

// CreateSavedFilter - Create a new saved filter
func (s *FiltersService) CreateSavedFilter(ctx context.Context, userID uuid.UUID, name string, resourceType string, filters map[string]string) (database.SavedFilters, error) {
	// Business logic: Validate filter name uniqueness per user + resource type
	existing, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{
		UserID:       pgtype.UUID{Bytes: userID, Valid: true},
		ResourceType: resourceType,
	})
	if err == nil {
		for _, f := range existing {
			if f.Name == name {
				return database.SavedFilters{}, fmt.Errorf("filter with name '%s' already exists for this resource type", name)
			}
		}
	}

	// Convert filters map to JSONB ([]byte)
	filtersJSON, err := json.Marshal(filters)
	if err != nil {
		return database.SavedFilters{}, fmt.Errorf("failed to marshal filters: %w", err)
	}

	filter, err := s.db.CreateSavedFilter(ctx, database.CreateSavedFilterParams{
		UserID:       pgtype.UUID{Bytes: userID, Valid: true},
		Name:         name,
		ResourceType: resourceType,
		Filters:      filtersJSON,
	})
	if err != nil {
		return database.SavedFilters{}, fmt.Errorf("failed to create saved filter: %w", err)
	}

	return filter, nil
}

// UpdateSavedFilter - Update an existing saved filter
func (s *FiltersService) UpdateSavedFilter(ctx context.Context, userID uuid.UUID, filterID uuid.UUID, name string, filters map[string]string) (database.SavedFilters, error) {
	// Business logic: Verify filter exists and belongs to user
	existing, err := s.db.GetSavedFilterByID(ctx, database.GetSavedFilterByIDParams{
		ID:     pgtype.UUID{Bytes: filterID, Valid: true},
		UserID: pgtype.UUID{Bytes: userID, Valid: true},
	})
	if err != nil {
		return database.SavedFilters{}, fmt.Errorf("filter not found or access denied: %w", err)
	}

	// Business logic: Check name uniqueness (excluding current filter)
	allFilters, err := s.db.GetSavedFilters(ctx, database.GetSavedFiltersParams{
		UserID:       pgtype.UUID{Bytes: userID, Valid: true},
		ResourceType: existing.ResourceType,
	})
	if err == nil {
		for _, f := range allFilters {
			existingID := uuid.Must(uuid.FromBytes(f.ID.Bytes[:]))
			if f.Name == name && existingID != filterID {
				return database.SavedFilters{}, fmt.Errorf("filter with name '%s' already exists for this resource type", name)
			}
		}
	}

	// Convert filters to JSONB
	filtersJSON, err := json.Marshal(filters)
	if err != nil {
		return database.SavedFilters{}, fmt.Errorf("failed to marshal filters: %w", err)
	}

	updated, err := s.db.UpdateSavedFilter(ctx, database.UpdateSavedFilterParams{
		ID:      pgtype.UUID{Bytes: filterID, Valid: true},
		UserID:  pgtype.UUID{Bytes: userID, Valid: true},
		Name:    name,
		Filters: filtersJSON,
	})
	if err != nil {
		return database.SavedFilters{}, fmt.Errorf("failed to update saved filter: %w", err)
	}

	return updated, nil
}

// DeleteSavedFilter - Delete a saved filter
func (s *FiltersService) DeleteSavedFilter(ctx context.Context, userID uuid.UUID, filterID uuid.UUID) error {
	_, err := s.db.DeleteSavedFilter(ctx, database.DeleteSavedFilterParams{
		ID:     pgtype.UUID{Bytes: filterID, Valid: true},
		UserID: pgtype.UUID{Bytes: userID, Valid: true},
	})
	if err != nil {
		return fmt.Errorf("failed to delete saved filter: %w", err)
	}
	return nil
}
