package com.storycove.service;

import com.storycove.config.SolrProperties;
import com.storycove.dto.AuthorSearchDto;
import com.storycove.dto.CollectionDto;
import com.storycove.dto.FacetCountDto;
import com.storycove.dto.SearchResultDto;
import com.storycove.dto.StorySearchDto;
import com.storycove.entity.Author;
import com.storycove.entity.Collection;
import com.storycove.entity.Story;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Service
@ConditionalOnProperty(
    value = "storycove.search.engine",
    havingValue = "solr",
    matchIfMissing = false
)
public class SolrService {

    private static final Logger logger = LoggerFactory.getLogger(SolrService.class);

    @Autowired(required = false)
    private SolrClient solrClient;

    @Autowired
    private SolrProperties properties;

    @Autowired
    @Lazy
    private ReadingTimeService readingTimeService;

    @Autowired
    private LibraryService libraryService;

    @PostConstruct
    public void initializeCores() {
        if (!isAvailable()) {
            logger.debug("Solr client not available - skipping core initialization");
            return;
        }

        try {
            logger.debug("Testing Solr cores availability...");
            testCoreAvailability(properties.getCores().getStories());
            testCoreAvailability(properties.getCores().getAuthors());
            testCoreAvailability(properties.getCores().getCollections());
            logger.debug("Solr cores are available");
        } catch (Exception e) {
            logger.error("Failed to test Solr cores availability", e);
        }
    }

    // ===============================
    // CORE MANAGEMENT
    // ===============================

    private void testCoreAvailability(String coreName) throws IOException, SolrServerException {
        SolrQuery query = new SolrQuery("*:*");
        query.setRows(0);
        QueryResponse response = solrClient.query(coreName, query);
        logger.debug("Core {} is available - found {} documents", coreName, response.getResults().getNumFound());
    }

    // ===============================
    // STORY INDEXING
    // ===============================

    public void indexStory(Story story) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping story indexing");
            return;
        }

        try {
            logger.debug("Indexing story: {} ({})", story.getTitle(), story.getId());
            SolrInputDocument doc = createStoryDocument(story);

            UpdateResponse response = solrClient.add(properties.getCores().getStories(), doc,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully indexed story: {}", story.getId());
            } else {
                logger.warn("Story indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to index story: {}", story.getId(), e);
            throw new IOException("Failed to index story", e);
        }
    }

    public void updateStory(Story story) throws IOException {
        // For Solr, update is the same as index (upsert behavior)
        indexStory(story);
    }

    public void deleteStory(UUID storyId) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping story deletion");
            return;
        }

        try {
            logger.debug("Deleting story from index: {}", storyId);
            UpdateResponse response = solrClient.deleteById(properties.getCores().getStories(),
                storyId.toString(), properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully deleted story: {}", storyId);
            } else {
                logger.warn("Story deletion returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to delete story: {}", storyId, e);
            throw new IOException("Failed to delete story", e);
        }
    }

    // ===============================
    // AUTHOR INDEXING
    // ===============================

    public void indexAuthor(Author author) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping author indexing");
            return;
        }

        try {
            logger.debug("Indexing author: {} ({})", author.getName(), author.getId());
            SolrInputDocument doc = createAuthorDocument(author);

            UpdateResponse response = solrClient.add(properties.getCores().getAuthors(), doc,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully indexed author: {}", author.getId());
            } else {
                logger.warn("Author indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to index author: {}", author.getId(), e);
            throw new IOException("Failed to index author", e);
        }
    }

    public void updateAuthor(Author author) throws IOException {
        // For Solr, update is the same as index (upsert behavior)
        indexAuthor(author);
    }

    public void deleteAuthor(UUID authorId) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping author deletion");
            return;
        }

        try {
            logger.debug("Deleting author from index: {}", authorId);
            UpdateResponse response = solrClient.deleteById(properties.getCores().getAuthors(),
                authorId.toString(), properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully deleted author: {}", authorId);
            } else {
                logger.warn("Author deletion returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to delete author: {}", authorId, e);
            throw new IOException("Failed to delete author", e);
        }
    }

    // ===============================
    // COLLECTION INDEXING
    // ===============================

    public void indexCollection(Collection collection) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping collection indexing");
            return;
        }

        try {
            logger.debug("Indexing collection: {} ({})", collection.getName(), collection.getId());
            SolrInputDocument doc = createCollectionDocument(collection);

            UpdateResponse response = solrClient.add(properties.getCores().getCollections(), doc,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully indexed collection: {}", collection.getId());
            } else {
                logger.warn("Collection indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to index collection: {}", collection.getId(), e);
            throw new IOException("Failed to index collection", e);
        }
    }

    public void updateCollection(Collection collection) throws IOException {
        // For Solr, update is the same as index (upsert behavior)
        indexCollection(collection);
    }

    public void deleteCollection(UUID collectionId) throws IOException {
        if (!isAvailable()) {
            logger.debug("Solr not available - skipping collection deletion");
            return;
        }

        try {
            logger.debug("Deleting collection from index: {}", collectionId);
            UpdateResponse response = solrClient.deleteById(properties.getCores().getCollections(),
                collectionId.toString(), properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully deleted collection: {}", collectionId);
            } else {
                logger.warn("Collection deletion returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to delete collection: {}", collectionId, e);
            throw new IOException("Failed to delete collection", e);
        }
    }

    // ===============================
    // BULK OPERATIONS
    // ===============================

    public void bulkIndexStories(List<Story> stories) throws IOException {
        if (!isAvailable() || stories.isEmpty()) {
            logger.debug("Solr not available or empty stories list - skipping bulk indexing");
            return;
        }

        try {
            logger.debug("Bulk indexing {} stories", stories.size());
            List<SolrInputDocument> docs = stories.stream()
                .map(this::createStoryDocument)
                .collect(Collectors.toList());

            UpdateResponse response = solrClient.add(properties.getCores().getStories(), docs,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully bulk indexed {} stories", stories.size());
            } else {
                logger.warn("Bulk story indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to bulk index stories", e);
            throw new IOException("Failed to bulk index stories", e);
        }
    }

    public void bulkIndexAuthors(List<Author> authors) throws IOException {
        if (!isAvailable() || authors.isEmpty()) {
            logger.debug("Solr not available or empty authors list - skipping bulk indexing");
            return;
        }

        try {
            logger.debug("Bulk indexing {} authors", authors.size());
            List<SolrInputDocument> docs = authors.stream()
                .map(this::createAuthorDocument)
                .collect(Collectors.toList());

            UpdateResponse response = solrClient.add(properties.getCores().getAuthors(), docs,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully bulk indexed {} authors", authors.size());
            } else {
                logger.warn("Bulk author indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to bulk index authors", e);
            throw new IOException("Failed to bulk index authors", e);
        }
    }

    public void bulkIndexCollections(List<Collection> collections) throws IOException {
        if (!isAvailable() || collections.isEmpty()) {
            logger.debug("Solr not available or empty collections list - skipping bulk indexing");
            return;
        }

        try {
            logger.debug("Bulk indexing {} collections", collections.size());
            List<SolrInputDocument> docs = collections.stream()
                .map(this::createCollectionDocument)
                .collect(Collectors.toList());

            UpdateResponse response = solrClient.add(properties.getCores().getCollections(), docs,
                properties.getCommit().getCommitWithin());

            if (response.getStatus() == 0) {
                logger.debug("Successfully bulk indexed {} collections", collections.size());
            } else {
                logger.warn("Bulk collection indexing returned non-zero status: {}", response.getStatus());
            }
        } catch (SolrServerException e) {
            logger.error("Failed to bulk index collections", e);
            throw new IOException("Failed to bulk index collections", e);
        }
    }

    // ===============================
    // DOCUMENT CREATION
    // ===============================

    private SolrInputDocument createStoryDocument(Story story) {
        SolrInputDocument doc = new SolrInputDocument();

        doc.addField("id", story.getId().toString());
        doc.addField("title", story.getTitle());
        doc.addField("description", story.getDescription());
        doc.addField("sourceUrl", story.getSourceUrl());
        doc.addField("coverPath", story.getCoverPath());
        doc.addField("wordCount", story.getWordCount());
        doc.addField("rating", story.getRating());
        doc.addField("volume", story.getVolume());
        doc.addField("isRead", story.getIsRead());
        doc.addField("readingPosition", story.getReadingPosition());
        doc.addField("readingProgressPercentage", calculateReadingProgressPercentage(story));

        if (story.getLastReadAt() != null) {
            doc.addField("lastReadAt", formatDateTime(story.getLastReadAt()));
        }

        if (story.getLastCompletedAt() != null) {
            doc.addField("lastCompletedAt", formatDateTime(story.getLastCompletedAt()));
        }

        if (story.getAuthor() != null) {
            doc.addField("authorId", story.getAuthor().getId().toString());
            doc.addField("authorName", story.getAuthor().getName());
        }

        if (story.getSeries() != null) {
            doc.addField("seriesId", story.getSeries().getId().toString());
            doc.addField("seriesName", story.getSeries().getName());
        }

        if (story.getTags() != null && !story.getTags().isEmpty()) {
            List<String> tagNames = story.getTags().stream()
                .map(tag -> tag.getName())
                .collect(Collectors.toList());
            doc.addField("tagNames", tagNames);
        }

        doc.addField("createdAt", formatDateTime(story.getCreatedAt()));
        doc.addField("updatedAt", formatDateTime(story.getUpdatedAt()));
        doc.addField("dateAdded", formatDateTime(story.getCreatedAt()));

        // Add library ID for multi-tenant separation
        String currentLibraryId = getCurrentLibraryId();
        try {
            if (currentLibraryId != null) {
                doc.addField("libraryId", currentLibraryId);
            }
        } catch (Exception e) {
            // If libraryId field doesn't exist, log warning and continue without it
            // This allows indexing to work even if schema migration hasn't completed
            logger.warn("Could not add libraryId field to document (field may not exist in schema): {}", e.getMessage());
        }

        // Add derived fields for statistics (Phase 1)
        addDerivedStatisticsFields(doc, story);

        return doc;
    }

    /**
     * Add derived fields to support statistics queries
     */
    private void addDerivedStatisticsFields(SolrInputDocument doc, Story story) {
        try {
            // Boolean flags for filtering
            doc.addField("hasDescription", story.getDescription() != null && !story.getDescription().trim().isEmpty());
            doc.addField("hasCoverImage", story.getCoverPath() != null && !story.getCoverPath().trim().isEmpty());
            doc.addField("hasRating", story.getRating() != null && story.getRating() > 0);

            // Extract source domain from URL
            if (story.getSourceUrl() != null && !story.getSourceUrl().trim().isEmpty()) {
                String domain = extractDomain(story.getSourceUrl());
                if (domain != null) {
                    doc.addField("sourceDomain", domain);
                }
            }

            // Tag count for statistics
            int tagCount = (story.getTags() != null) ? story.getTags().size() : 0;
            doc.addField("tagCount", tagCount);

        } catch (Exception e) {
            // Don't fail indexing if derived fields can't be added
            logger.debug("Could not add some derived statistics fields: {}", e.getMessage());
        }
    }

    /**
     * Extract domain from URL for source statistics
     */
    private String extractDomain(String url) {
        try {
            if (url == null || url.trim().isEmpty()) {
                return null;
            }

            // Handle URLs without protocol
            if (!url.startsWith("http://") && !url.startsWith("https://")) {
                url = "https://" + url;
            }

            java.net.URL parsedUrl = new java.net.URL(url);
            String host = parsedUrl.getHost();

            // Remove www. prefix if present
            if (host.startsWith("www.")) {
                host = host.substring(4);
            }

            return host;
        } catch (Exception e) {
            logger.debug("Failed to extract domain from URL: {}", url);
            return null;
        }
    }

    private SolrInputDocument createAuthorDocument(Author author) {
        SolrInputDocument doc = new SolrInputDocument();

        doc.addField("id", author.getId().toString());
        doc.addField("name", author.getName());
        doc.addField("notes", author.getNotes());
        doc.addField("authorRating", author.getAuthorRating());
        doc.addField("avatarImagePath", author.getAvatarImagePath());

        if (author.getUrls() != null && !author.getUrls().isEmpty()) {
            doc.addField("urls", author.getUrls());
        }

        // Calculate derived fields
        if (author.getStories() != null) {
            doc.addField("storyCount", author.getStories().size());

            OptionalDouble avgRating = author.getStories().stream()
                .filter(story -> story.getRating() != null && story.getRating() > 0)
                .mapToInt(Story::getRating)
                .average();

            if (avgRating.isPresent()) {
                doc.addField("averageStoryRating", avgRating.getAsDouble());
            }
        }

        doc.addField("createdAt", formatDateTime(author.getCreatedAt()));
        doc.addField("updatedAt", formatDateTime(author.getUpdatedAt()));

        // Add library ID for multi-tenant separation
        String currentLibraryId = getCurrentLibraryId();
        try {
            if (currentLibraryId != null) {
                doc.addField("libraryId", currentLibraryId);
            }
        } catch (Exception e) {
            // If libraryId field doesn't exist, log warning and continue without it
            // This allows indexing to work even if schema migration hasn't completed
            logger.warn("Could not add libraryId field to document (field may not exist in schema): {}", e.getMessage());
        }

        return doc;
    }

    private SolrInputDocument createCollectionDocument(Collection collection) {
        SolrInputDocument doc = new SolrInputDocument();

        doc.addField("id", collection.getId().toString());
        doc.addField("name", collection.getName());
        doc.addField("description", collection.getDescription());
        doc.addField("rating", collection.getRating());
        doc.addField("coverImagePath", collection.getCoverImagePath());
        doc.addField("isArchived", collection.getIsArchived());

        // Calculate derived fields
        doc.addField("storyCount", collection.getStoryCount());
        doc.addField("totalWordCount", collection.getTotalWordCount());
        doc.addField("estimatedReadingTime", collection.getEstimatedReadingTime());

        Double avgRating = collection.getAverageStoryRating();
        if (avgRating != null && avgRating > 0) {
            doc.addField("averageStoryRating", avgRating);
        }

        // Handle tags
        if (collection.getTags() != null && !collection.getTags().isEmpty()) {
            List<String> tagNames = collection.getTags().stream()
                .map(tag -> tag.getName())
                .collect(Collectors.toList());
            doc.addField("tagNames", tagNames);
        }

        doc.addField("createdAt", formatDateTime(collection.getCreatedAt()));
        doc.addField("updatedAt", formatDateTime(collection.getUpdatedAt()));

        // Add library ID for multi-tenant separation
        String currentLibraryId = getCurrentLibraryId();
        try {
            if (currentLibraryId != null) {
                doc.addField("libraryId", currentLibraryId);
            }
        } catch (Exception e) {
            // If libraryId field doesn't exist, log warning and continue without it
            // This allows indexing to work even if schema migration hasn't completed
            logger.warn("Could not add libraryId field to document (field may not exist in schema): {}", e.getMessage());
        }

        return doc;
    }

    private String formatDateTime(LocalDateTime dateTime) {
        if (dateTime == null) return null;
        return dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "Z";
    }

    private Integer calculateReadingProgressPercentage(Story story) {
        if (story.getReadingPosition() == null || story.getReadingPosition() == 0) {
            return 0;
        }

        // ALWAYS use contentHtml for consistency (frontend uses contentHtml for position tracking)
        int totalLength = 0;
        if (story.getContentHtml() != null && !story.getContentHtml().isEmpty()) {
            totalLength = story.getContentHtml().length();
        }

        if (totalLength == 0) {
            return 0;
        }

        // Calculate percentage and round to nearest integer
        int percentage = Math.round((float) story.getReadingPosition() * 100 / totalLength);
        return Math.min(100, percentage);
    }

    // ===============================
    // UTILITY METHODS
    // ===============================

    public boolean isAvailable() {
        return solrClient != null;
    }

    /**
     * Get current library ID for multi-tenant document separation.
     * Falls back to "default" if no library is active.
     */
    private String getCurrentLibraryId() {
        try {
            String libraryId = libraryService.getCurrentLibraryId();
            return libraryId != null ? libraryId : "default";
        } catch (Exception e) {
            logger.warn("Could not get current library ID, using 'default': {}", e.getMessage());
            return "default";
        }
    }

    public boolean testConnection() {
        if (!isAvailable()) {
            return false;
        }

        try {
            // Test connection by pinging a core
            testCoreAvailability(properties.getCores().getStories());
            return true;
        } catch (Exception e) {
            logger.debug("Solr connection test failed", e);
            return false;
        }
    }

    /**
     * Add libraryId field to Solr schema via Schema API
     * This is required for library-aware indexing in production environments
     */
    public void addLibraryIdField() throws Exception {
        if (!isAvailable()) {
            throw new IllegalStateException("Solr is not available");
        }

        try {
            // Check if libraryId field already exists
            if (hasLibraryIdField()) {
                logger.info("libraryId field already exists in schema");
                return;
            }

            logger.info("Adding libraryId field to Solr schema via Schema API");

            // Add field to stories core
            try {
                var storiesRequest = new org.apache.solr.client.solrj.request.schema.SchemaRequest.AddField(
                    Map.of(
                        "name", "libraryId",
                        "type", "string",
                        "indexed", true,
                        "stored", true,
                        "required", false
                    )
                );
                var storiesResponse = storiesRequest.process(solrClient, properties.getCores().getStories());
                logger.info("Added libraryId field to stories core: {}", storiesResponse.getStatus());
            } catch (Exception e) {
                if (e.getMessage() != null && e.getMessage().contains("already exists")) {
                    logger.info("libraryId field already exists in stories core");
                } else {
                    throw e;
                }
            }

            // Add field to authors core
            try {
                var authorsRequest = new org.apache.solr.client.solrj.request.schema.SchemaRequest.AddField(
                    Map.of(
                        "name", "libraryId",
                        "type", "string",
                        "indexed", true,
                        "stored", true,
                        "required", false
                    )
                );
                var authorsResponse = authorsRequest.process(solrClient, properties.getCores().getAuthors());
                logger.info("Added libraryId field to authors core: {}", authorsResponse.getStatus());
            } catch (Exception e) {
                if (e.getMessage() != null && e.getMessage().contains("already exists")) {
                    logger.info("libraryId field already exists in authors core");
                } else {
                    throw e;
                }
            }

            logger.info("Successfully added libraryId field to both cores");

        } catch (Exception e) {
            logger.error("Failed to add libraryId field to schema", e);
            throw new RuntimeException("Failed to add libraryId field to schema: " + e.getMessage(), e);
        }
    }

    /**
     * Check if libraryId field exists in the schema
     */
    public boolean hasLibraryIdField() {
        try {
            var request = new org.apache.solr.client.solrj.request.schema.SchemaRequest.Field("libraryId");
            request.process(solrClient, properties.getCores().getStories());
            return true;
        } catch (Exception e) {
            // Field doesn't exist or other error
            return false;
        }
    }

    /**
     * Clear all documents from both stories and authors cores
     * Used for migration when core recreation isn't possible
     */
    public void clearAllDocuments() throws Exception {
        if (!isAvailable()) {
            throw new IllegalStateException("Solr is not available");
        }

        try {
            logger.info("Clearing all documents from Solr cores");

            // Clear stories core
            try {
                solrClient.deleteByQuery(properties.getCores().getStories(), "*:*",
                    properties.getCommit().getCommitWithin());
                logger.info("Cleared all documents from stories core");
            } catch (Exception e) {
                logger.error("Failed to clear stories core", e);
                throw e;
            }

            // Clear authors core
            try {
                solrClient.deleteByQuery(properties.getCores().getAuthors(), "*:*",
                    properties.getCommit().getCommitWithin());
                logger.info("Cleared all documents from authors core");
            } catch (Exception e) {
                logger.error("Failed to clear authors core", e);
                throw e;
            }

            // Clear collections core
            try {
                solrClient.deleteByQuery(properties.getCores().getCollections(), "*:*",
                    properties.getCommit().getCommitWithin());
                logger.info("Cleared all documents from collections core");
            } catch (Exception e) {
                logger.warn("Failed to clear collections core (may not exist yet): {}", e.getMessage());
            }

            logger.info("Successfully cleared all documents from Solr cores");

        } catch (Exception e) {
            logger.error("Failed to clear all documents", e);
            throw new RuntimeException("Failed to clear all documents: " + e.getMessage(), e);
        }
    }

    // ===============================
    // SEARCH OPERATIONS
    // ===============================

    public SearchResultDto<StorySearchDto> searchStories(String query, List<String> tags, String author,
                                                         String series, Integer minWordCount, Integer maxWordCount,
                                                         Float minRating, Boolean isRead, Boolean isFavorite,
                                                         String sortBy, String sortOrder, int page, int size,
                                                         List<String> facetBy,
                                                         // Advanced filters
                                                         String createdAfter, String createdBefore,
                                                         String lastReadAfter, String lastReadBefore,
                                                         Boolean unratedOnly, String readingStatus,
                                                         Boolean hasReadingProgress, Boolean hasCoverImage,
                                                         String sourceDomain, String seriesFilter,
                                                         Integer minTagCount, Boolean popularOnly,
                                                         Boolean hiddenGemsOnly) {
        if (!isAvailable()) {
            logger.debug("Solr not available - returning empty search results");
            return new SearchResultDto<StorySearchDto>(Collections.emptyList(), 0, 0, Collections.emptyList());
        }

        try {
            SolrQuery solrQuery = new SolrQuery();

            // Set query
            if (query == null || query.trim().isEmpty()) {
                solrQuery.setQuery("*:*");
            } else {
                // Use dismax query parser for better relevance
                solrQuery.setQuery(query);
                solrQuery.set("defType", "edismax");
                solrQuery.set("qf", "title^3.0 description^2.0 authorName^2.0 seriesName^1.5 tagNames^1.0");
                solrQuery.set("mm", "1<-1 5<-2 6<90%"); // Minimum should match
            }

            // Apply filters
            applySearchFilters(solrQuery, tags, author, series, minWordCount, maxWordCount,
                minRating, isRead, isFavorite, createdAfter, createdBefore, lastReadAfter,
                lastReadBefore, unratedOnly, readingStatus, hasReadingProgress, hasCoverImage,
                sourceDomain, seriesFilter, minTagCount, popularOnly, hiddenGemsOnly);

            // Pagination
            solrQuery.setStart(page * size);
            solrQuery.setRows(size);

            // Sorting
            if (sortBy != null && !sortBy.isEmpty()) {
                SolrQuery.ORDER order = "desc".equalsIgnoreCase(sortOrder) ?
                    SolrQuery.ORDER.desc : SolrQuery.ORDER.asc;
                solrQuery.setSort(sortBy, order);
            } else {
                // Default relevance sorting
                solrQuery.setSort("score", SolrQuery.ORDER.desc);
            }

            // Enable highlighting
            if (properties.getQuery().isHighlight()) {
                solrQuery.setHighlight(true);
                solrQuery.addHighlightField("title");
                solrQuery.addHighlightField("description");
                solrQuery.setHighlightSimplePre("<em>");
                solrQuery.setHighlightSimplePost("</em>");
                solrQuery.setHighlightFragsize(150);
            }

            // Enable faceting
            if (properties.getQuery().isFacets()) {
                solrQuery.setFacet(true);
                // Use optimized facet fields for better performance
                solrQuery.addFacetField("authorName_facet");
                solrQuery.addFacetField("tagNames_facet");
                solrQuery.addFacetField("seriesName_facet");
                solrQuery.addFacetField("rating");
                solrQuery.addFacetField("isRead");
                solrQuery.setFacetMinCount(1);
                solrQuery.setFacetSort("count");
                solrQuery.setFacetLimit(100); // Limit facet results for performance
            }

            // Debug: Log the query being sent to Solr
            logger.info("SolrService: Executing Solr query: {}", solrQuery);

            QueryResponse response = solrClient.query(properties.getCores().getStories(), solrQuery);
            logger.info("SolrService: Query executed successfully, found {} results", response.getResults().getNumFound());

            return buildStorySearchResult(response);

        } catch (Exception e) {
            logger.error("Story search failed for query: {}", query, e);
            return new SearchResultDto<StorySearchDto>(Collections.emptyList(), 0, 0, Collections.emptyList());
        }
    }

    public List<AuthorSearchDto> searchAuthors(String query, int limit) {
        if (!isAvailable()) {
            logger.debug("Solr not available - returning empty author search results");
            return Collections.emptyList();
        }

        try {
            SolrQuery solrQuery = new SolrQuery();

            // Set query
            if (query == null || query.trim().isEmpty()) {
                solrQuery.setQuery("*:*");
            } else {
                solrQuery.setQuery(query);
                solrQuery.set("defType", "edismax");
                solrQuery.set("qf", "name^3.0 notes^1.0 urls^0.5");
            }

            solrQuery.setRows(limit);

            // Add library filter for multi-tenant separation
            String currentLibraryId = getCurrentLibraryId();
            solrQuery.addFilterQuery("libraryId:\"" + escapeQueryChars(currentLibraryId) + "\"");

            // Sort by storyCount if available, otherwise by name
            solrQuery.setSort("storyCount", SolrQuery.ORDER.desc);
            solrQuery.addSort("name", SolrQuery.ORDER.asc);

            // Explicitly disable faceting to avoid range faceting issues
            solrQuery.setFacet(false);

            QueryResponse response = solrClient.query(properties.getCores().getAuthors(), solrQuery);

            return buildAuthorSearchResults(response);

        } catch (Exception e) {
            logger.error("Author search failed for query: {}", query, e);
            return Collections.emptyList();
        }
    }

    public SearchResultDto<CollectionDto> searchCollections(String query, List<String> tags,
                                                            boolean includeArchived, int page, int limit) {
        if (!isAvailable()) {
            logger.debug("Solr not available - returning empty collection search results");
            return new SearchResultDto<>(new ArrayList<>(), 0, page, limit, query != null ? query : "", 0);
        }

        try {
            SolrQuery solrQuery = new SolrQuery();

            // Set query
            if (query == null || query.trim().isEmpty()) {
                solrQuery.setQuery("*:*");
            } else {
                solrQuery.setQuery(query);
                solrQuery.set("defType", "edismax");
                solrQuery.set("qf", "name^3.0 description^2.0 tagNames^1.0");
            }

            // Add library filter for multi-tenant separation
            String currentLibraryId = getCurrentLibraryId();
            solrQuery.addFilterQuery("libraryId:\"" + escapeQueryChars(currentLibraryId) + "\"");

            // Tag filters
            if (tags != null && !tags.isEmpty()) {
                String tagFilter = tags.stream()
                    .map(tag -> "tagNames:\"" + escapeQueryChars(tag) + "\"")
                    .collect(Collectors.joining(" AND "));
                solrQuery.addFilterQuery(tagFilter);
            }

            // Archive filter
            if (!includeArchived) {
                solrQuery.addFilterQuery("isArchived:false");
            }

            // Pagination
            solrQuery.setStart(page * limit);
            solrQuery.setRows(limit);

            // Sorting - by name ascending (name_sort is a docValues string copy of name)
            solrQuery.setSort("name_sort", SolrQuery.ORDER.asc);

            // Explicitly disable faceting
            solrQuery.setFacet(false);

            logger.info("SolrService: Executing Collection search query: {}", solrQuery);

            QueryResponse response = solrClient.query(properties.getCores().getCollections(), solrQuery);

            logger.info("SolrService: Collection query executed successfully, found {} results",
                response.getResults().getNumFound());

            return buildCollectionSearchResult(response, page, limit, query);

        } catch (Exception e) {
            logger.error("Collection search failed for query: {}", query, e);
            return new SearchResultDto<>(new ArrayList<>(), 0, page, limit, query != null ? query : "", 0);
        }
    }

    public List<String> getTagSuggestions(String query, int limit) {
        if (!isAvailable()) {
            return Collections.emptyList();
        }

        try {
            SolrQuery solrQuery = new SolrQuery();
            solrQuery.setQuery("tagNames:*" + query + "*");
            solrQuery.setRows(0);
            solrQuery.setFacet(true);
            solrQuery.addFacetField("tagNames_facet");
            solrQuery.setFacetMinCount(1);
            solrQuery.setFacetLimit(limit);

            // Add library filter for multi-tenant separation
            String currentLibraryId = getCurrentLibraryId();
            solrQuery.addFilterQuery("libraryId:\"" + escapeQueryChars(currentLibraryId) + "\"");

            QueryResponse response = solrClient.query(properties.getCores().getStories(), solrQuery);

            return response.getFacetField("tagNames_facet").getValues().stream()
                .map(facet -> facet.getName())
                .filter(name -> name.toLowerCase().contains(query.toLowerCase()))
                .collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("Tag suggestions failed for query: {}", query, e);
            return Collections.emptyList();
        }
    }

    public String getRandomStoryId(Long seed) {
        if (!isAvailable()) {
            return null;
        }

        try {
            SolrQuery solrQuery = new SolrQuery("*:*");
            solrQuery.setRows(1);
            if (seed != null) {
                solrQuery.setSort("random_" + seed, SolrQuery.ORDER.asc);
            } else {
                solrQuery.setSort("random_" + System.currentTimeMillis(), SolrQuery.ORDER.asc);
            }

            // Add library filter for multi-tenant separation
            String currentLibraryId = getCurrentLibraryId();
            solrQuery.addFilterQuery("libraryId:\"" + escapeQueryChars(currentLibraryId) + "\"");

            QueryResponse response = solrClient.query(properties.getCores().getStories(), solrQuery);

            if (response.getResults().size() > 0) {
                return (String) response.getResults().get(0).getFieldValue("id");
            }

        } catch (Exception e) {
            logger.error("Random story ID retrieval failed", e);
        }

        return null;
    }

    // ===============================
    // RESULT BUILDING
    // ===============================

    private SearchResultDto<StorySearchDto> buildStorySearchResult(QueryResponse response) {
        SolrDocumentList results = response.getResults();
        List<StorySearchDto> stories = new ArrayList<>();

        for (SolrDocument doc : results) {
            StorySearchDto story = convertToStorySearchDto(doc);

            // Add highlights
            if (response.getHighlighting() != null) {
                String id = (String) doc.getFieldValue("id");
                Map<String, List<String>> highlights = response.getHighlighting().get(id);
                if (highlights != null) {
                    List<String> allHighlights = highlights.values().stream()
                        .flatMap(List::stream)
                        .collect(Collectors.toList());
                    story.setHighlights(allHighlights);
                }
            }

            stories.add(story);
        }

        // Build facets organized by field name
        Map<String, List<FacetCountDto>> facetsMap = new HashMap<>();
        if (response.getFacetFields() != null) {
            response.getFacetFields().forEach(facetField -> {
                String fieldName = facetField.getName();
                List<FacetCountDto> fieldFacets = new ArrayList<>();

                facetField.getValues().forEach(count -> {
                    fieldFacets.add(new FacetCountDto(count.getName(), (int) count.getCount()));
                });

                facetsMap.put(fieldName, fieldFacets);
            });
        }

        SearchResultDto<StorySearchDto> result = new SearchResultDto<StorySearchDto>(stories, (int) results.getNumFound(),
            0, stories.size(), "", 0, facetsMap);
        return result;
    }

    private List<AuthorSearchDto> buildAuthorSearchResults(QueryResponse response) {
        return response.getResults().stream()
            .map(this::convertToAuthorSearchDto)
            .collect(Collectors.toList());
    }

    private SearchResultDto<CollectionDto> buildCollectionSearchResult(QueryResponse response, int page, int limit, String query) {
        SolrDocumentList results = response.getResults();
        List<CollectionDto> collections = new ArrayList<>();

        for (SolrDocument doc : results) {
            CollectionDto collection = convertToCollectionDto(doc);
            collections.add(collection);
        }

        return new SearchResultDto<>(collections, (int) results.getNumFound(), page, limit,
            query != null ? query : "", 0);
    }

    private StorySearchDto convertToStorySearchDto(SolrDocument doc) {
        StorySearchDto story = new StorySearchDto();

        story.setId(UUID.fromString((String) doc.getFieldValue("id")));
        story.setTitle((String) doc.getFieldValue("title"));
        story.setDescription((String) doc.getFieldValue("description"));
        story.setSourceUrl((String) doc.getFieldValue("sourceUrl"));
        story.setCoverPath((String) doc.getFieldValue("coverPath"));
        story.setWordCount((Integer) doc.getFieldValue("wordCount"));
        story.setRating((Integer) doc.getFieldValue("rating"));
        Number volumeVal = (Number) doc.getFieldValue("volume");
        story.setVolume(volumeVal != null ? volumeVal.doubleValue() : null);
        story.setIsRead((Boolean) doc.getFieldValue("isRead"));
        story.setReadingPosition((Integer) doc.getFieldValue("readingPosition"));
        story.setReadingProgressPercentage((Integer) doc.getFieldValue("readingProgressPercentage"));

        // Handle dates
        story.setLastReadAt(parseDateTimeFromSolr(doc.getFieldValue("lastReadAt")));
        story.setLastCompletedAt(parseDateTimeFromSolr(doc.getFieldValue("lastCompletedAt")));
        story.setCreatedAt(parseDateTimeFromSolr(doc.getFieldValue("createdAt")));
        story.setUpdatedAt(parseDateTimeFromSolr(doc.getFieldValue("updatedAt")));
        story.setDateAdded(parseDateTimeFromSolr(doc.getFieldValue("dateAdded")));

        // Handle author
        String authorIdStr = (String) doc.getFieldValue("authorId");
        if (authorIdStr != null) {
            story.setAuthorId(UUID.fromString(authorIdStr));
        }
        story.setAuthorName((String) doc.getFieldValue("authorName"));

        // Handle series
        String seriesIdStr = (String) doc.getFieldValue("seriesId");
        if (seriesIdStr != null) {
            story.setSeriesId(UUID.fromString(seriesIdStr));
        }
        story.setSeriesName((String) doc.getFieldValue("seriesName"));

        // Handle tags
        java.util.Collection<Object> tagValues = doc.getFieldValues("tagNames");
        if (tagValues != null) {
            List<String> tagNames = tagValues.stream()
                .map(Object::toString)
                .collect(Collectors.toList());
            story.setTagNames(tagNames);
        }

        return story;
    }

    private AuthorSearchDto convertToAuthorSearchDto(SolrDocument doc) {
        AuthorSearchDto author = new AuthorSearchDto();

        author.setId(UUID.fromString((String) doc.getFieldValue("id")));
        author.setName((String) doc.getFieldValue("name"));
        author.setNotes((String) doc.getFieldValue("notes"));
        author.setAuthorRating((Integer) doc.getFieldValue("authorRating"));
        author.setAvatarImagePath((String) doc.getFieldValue("avatarImagePath"));
        author.setStoryCount((Integer) doc.getFieldValue("storyCount"));

        Double avgRating = (Double) doc.getFieldValue("averageStoryRating");
        if (avgRating != null) {
            author.setAverageStoryRating(avgRating);
        }

        // Handle URLs
        java.util.Collection<Object> urlValues = doc.getFieldValues("urls");
        if (urlValues != null) {
            List<String> urls = urlValues.stream()
                .map(Object::toString)
                .collect(Collectors.toList());
            author.setUrls(urls);
        }

        // Handle dates
        author.setCreatedAt(parseDateTimeFromSolr(doc.getFieldValue("createdAt")));
        author.setUpdatedAt(parseDateTimeFromSolr(doc.getFieldValue("updatedAt")));

        return author;
    }

    private CollectionDto convertToCollectionDto(SolrDocument doc) {
        CollectionDto collection = new CollectionDto();

        collection.setId(UUID.fromString((String) doc.getFieldValue("id")));
        collection.setName((String) doc.getFieldValue("name"));
        collection.setDescription((String) doc.getFieldValue("description"));
        collection.setRating((Integer) doc.getFieldValue("rating"));
        collection.setCoverImagePath((String) doc.getFieldValue("coverImagePath"));
        collection.setIsArchived((Boolean) doc.getFieldValue("isArchived"));
        collection.setStoryCount((Integer) doc.getFieldValue("storyCount"));
        Number totalWordCountVal = (Number) doc.getFieldValue("totalWordCount");
        collection.setTotalWordCount(totalWordCountVal != null ? totalWordCountVal.intValue() : null);
        collection.setEstimatedReadingTime((Integer) doc.getFieldValue("estimatedReadingTime"));

        Double avgRating = (Double) doc.getFieldValue("averageStoryRating");
        if (avgRating != null) {
            collection.setAverageStoryRating(avgRating);
        }

        // Handle tags
        java.util.Collection<Object> tagValues = doc.getFieldValues("tagNames");
        if (tagValues != null) {
            List<String> tagNames = tagValues.stream()
                .map(Object::toString)
                .collect(Collectors.toList());
            collection.setTagNames(tagNames);
        }

        // Handle dates
        collection.setCreatedAt(parseDateTimeFromSolr(doc.getFieldValue("createdAt")));
        collection.setUpdatedAt(parseDateTimeFromSolr(doc.getFieldValue("updatedAt")));

        return collection;
    }

    private LocalDateTime parseDateTime(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) {
            return null;
        }
        try {
            // Remove 'Z' suffix if present and parse
            String cleanDate = dateStr.endsWith("Z") ? dateStr.substring(0, dateStr.length() - 1) : dateStr;
            return LocalDateTime.parse(cleanDate, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
        } catch (Exception e) {
            logger.warn("Failed to parse date: {}", dateStr, e);
            return null;
        }
    }

    private LocalDateTime parseDateTimeFromSolr(Object dateValue) {
        if (dateValue == null) {
            return null;
        }
        if (dateValue instanceof Date) {
            // Convert java.util.Date to LocalDateTime
            return ((Date) dateValue).toInstant()
                .atZone(java.time.ZoneId.systemDefault())
                .toLocalDateTime();
        } else if (dateValue instanceof String) {
            return parseDateTime((String) dateValue);
        } else {
            logger.warn("Unexpected date type: {}", dateValue.getClass());
            return null;
        }
    }

    public List<StorySearchDto> getRandomStories(int count, List<String> tags, String author,
                                                String series, Integer minWordCount, Integer maxWordCount,
                                                Float minRating, Boolean isRead, Boolean isFavorite,
                                                Long seed) {
        if (!isAvailable()) {
            return Collections.emptyList();
        }

        try {
            SolrQuery solrQuery = new SolrQuery("*:*");

            // Apply filters
            applySearchFilters(solrQuery, tags, author, series, minWordCount, maxWordCount,
                minRating, isRead, isFavorite, null, null, null, null, null, null,
                null, null, null, null, null, null, null);

            solrQuery.setRows(count);

            // Use random sorting
            if (seed != null) {
                solrQuery.setSort("random_" + seed, SolrQuery.ORDER.asc);
            } else {
                solrQuery.setSort("random_" + System.currentTimeMillis(), SolrQuery.ORDER.asc);
            }

            QueryResponse response = solrClient.query(properties.getCores().getStories(), solrQuery);

            return response.getResults().stream()
                .map(this::convertToStorySearchDto)
                .collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("Random stories retrieval failed", e);
            return Collections.emptyList();
        }
    }

    // ===============================
    // FILTER APPLICATION
    // ===============================

    private void applySearchFilters(SolrQuery solrQuery, List<String> tags, String author,
                                   String series, Integer minWordCount, Integer maxWordCount,
                                   Float minRating, Boolean isRead, Boolean isFavorite,
                                   String createdAfter, String createdBefore,
                                   String lastReadAfter, String lastReadBefore,
                                   Boolean unratedOnly, String readingStatus,
                                   Boolean hasReadingProgress, Boolean hasCoverImage,
                                   String sourceDomain, String seriesFilter,
                                   Integer minTagCount, Boolean popularOnly,
                                   Boolean hiddenGemsOnly) {

        List<String> filters = new ArrayList<>();

        // Library filter - ensure multi-tenant data separation
        String currentLibraryId = getCurrentLibraryId();
        filters.add("libraryId:\"" + escapeQueryChars(currentLibraryId) + "\"");

        // Tag filters - use facet field for exact matching
        if (tags != null && !tags.isEmpty()) {
            String tagFilter = tags.stream()
                .map(tag -> "tagNames_facet:\"" + escapeQueryChars(tag) + "\"")
                .collect(Collectors.joining(" AND "));
            filters.add("(" + tagFilter + ")");
        }

        // Author filter - use facet field for exact matching
        if (author != null && !author.trim().isEmpty()) {
            filters.add("authorName_facet:\"" + escapeQueryChars(author.trim()) + "\"");
        }

        // Series filter - use facet field for exact matching
        if (series != null && !series.trim().isEmpty()) {
            filters.add("seriesName_facet:\"" + escapeQueryChars(series.trim()) + "\"");
        }

        // Word count filters
        if (minWordCount != null && maxWordCount != null) {
            filters.add("wordCount:[" + minWordCount + " TO " + maxWordCount + "]");
        } else if (minWordCount != null) {
            filters.add("wordCount:[" + minWordCount + " TO *]");
        } else if (maxWordCount != null) {
            filters.add("wordCount:[* TO " + maxWordCount + "]");
        }

        // Rating filter
        if (minRating != null) {
            filters.add("rating:[" + minRating.intValue() + " TO *]");
        }

        // Read status filter
        if (isRead != null) {
            filters.add("isRead:" + isRead);
        }

        // Date filters - convert to ISO format for Solr
        if (createdAfter != null) {
            filters.add("createdAt:[" + formatDateForSolr(createdAfter) + " TO *]");
        }
        if (createdBefore != null) {
            filters.add("createdAt:[* TO " + formatDateForSolr(createdBefore) + "]");
        }

        // Last read date filters
        if (lastReadAfter != null) {
            filters.add("lastReadAt:[" + formatDateForSolr(lastReadAfter) + " TO *]");
        }
        if (lastReadBefore != null) {
            filters.add("lastReadAt:[* TO " + formatDateForSolr(lastReadBefore) + "]");
        }

        // Unrated filter
        if (unratedOnly != null && unratedOnly) {
            filters.add("(-rating:[1 TO *] OR rating:0)");
        }

        // Cover image filter
        if (hasCoverImage != null) {
            if (hasCoverImage) {
                filters.add("coverPath:[\"\" TO *]"); // Has non-empty value
            } else {
                filters.add("-coverPath:[\"\" TO *]"); // No value or empty
            }
        }

        // Reading status filter
        if (readingStatus != null && !readingStatus.equals("all")) {
            switch (readingStatus) {
                case "unread":
                    // Unread: isRead is false AND readingPosition is 0 (or null)
                    filters.add("isRead:false AND (readingPosition:0 OR (*:* -readingPosition:[1 TO *]))");
                    break;
                case "started":
                    // Started: has reading progress but not finished
                    filters.add("readingPosition:[1 TO *] AND isRead:false");
                    break;
                case "completed":
                    // Completed: isRead is true
                    filters.add("isRead:true");
                    break;
            }
        }

        // Reading progress filter
        if (hasReadingProgress != null) {
            if (hasReadingProgress) {
                filters.add("readingPosition:[1 TO *]"); // Has reading progress
            } else {
                filters.add("(readingPosition:0 OR (*:* -readingPosition:[1 TO *]))"); // No reading progress
            }
        }

        // Series filter
        if (seriesFilter != null && !seriesFilter.equals("all")) {
            switch (seriesFilter) {
                case "standalone":
                    // Standalone: no series (seriesId is null or empty)
                    filters.add("(*:* -seriesId:[\"\" TO *])");
                    break;
                case "series":
                    // Part of series: has seriesId
                    filters.add("seriesId:[\"\" TO *]");
                    break;
                case "firstInSeries":
                    // First in series: has seriesId and volume is 1
                    filters.add("seriesId:[\"\" TO *] AND volume:1");
                    break;
                case "lastInSeries":
                    // This would require complex logic to determine last volume per series
                    // For now, just filter by having a series (can be enhanced later)
                    filters.add("seriesId:[\"\" TO *]");
                    break;
            }
        }

        // Source domain filter
        if (sourceDomain != null && !sourceDomain.trim().isEmpty()) {
            // Extract domain from sourceUrl field
            filters.add("sourceUrl:*" + escapeQueryChars(sourceDomain.trim()) + "*");
        }

        // Apply all filters
        for (String filter : filters) {
            solrQuery.addFilterQuery(filter);
        }
    }

    public void recreateCores() throws IOException {
        if (!isAvailable()) {
            throw new IOException("Solr is not available");
        }

        List<String> coreNames = Arrays.asList(
            properties.getCores().getStories(),
            properties.getCores().getAuthors(),
            properties.getCores().getCollections()
        );

        for (String coreName : coreNames) {
            try {
                logger.info("Clearing Lucene index for core: {}", coreName);
                // Delete all documents and hard-commit so the index is empty immediately.
                // Using deleteByQuery keeps the core registered in Solr — no restart needed.
                solrClient.deleteByQuery(coreName, "*:*");
                solrClient.commit(coreName);
                logger.info("Successfully cleared index for core: {}", coreName);
            } catch (Exception e) {
                logger.warn("Could not clear core {} (may not exist yet): {}", coreName, e.getMessage());
            }
        }
    }

    public void recreateIndices() throws IOException {
        recreateCores();
    }

    /**
     * Escape special characters in Solr query strings
     */
    private String escapeQueryChars(String s) {
        if (s == null) return null;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // These characters are part of the query syntax and must be escaped
            if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
                || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
                || c == '*' || c == '?' || c == '|' || c == '&' || c == ';' || c == '/'
                || Character.isWhitespace(c)) {
                sb.append('\\');
            }
            sb.append(c);
        }
        return sb.toString();
    }

    /**
     * Format date string for Solr queries
     */
    private String formatDateForSolr(String dateStr) {
        if (dateStr == null || dateStr.isEmpty()) {
            return dateStr;
        }

        try {
            // If it's already in ISO format, return as-is
            if (dateStr.contains("T") || dateStr.endsWith("Z")) {
                return dateStr;
            }

            // Convert date string like "2025-08-23" to ISO format "2025-08-23T00:00:00Z"
            if (dateStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
                return dateStr + "T00:00:00Z";
            }

            return dateStr;
        } catch (Exception e) {
            logger.warn("Failed to format date for Solr: {}", dateStr, e);
            return dateStr;
        }
    }
}