/**
 * Copyright (c) 2025 SoftCorporation LLC. All rights reserved.
 *
 * The Software License, Version 1.0
 *
 * SoftCorporation LLC. grants you ("Licensee") a non-exclusive, royalty free,
 * license to use, modify and redistribute this software in source and binary
 * code form, provided that the following conditions are met:
 *
 * 1. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        SoftCorporation LLC. (http://www.softcorporation.com)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 2. This software and SoftCorporation name must not be used to
 *    promote products derived from the software without prior
 *    written permission. For written permission, please contact
 *    info@softcorporation.com.
 *
 * This software is provided "AS IS," without a warranty of any kind.
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 * IN NO EVENT SHALL THE SOFTCORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION).
 *
 */
package com.softcorporation.sandi.index.service.solr;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.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.stereotype.Service;

import com.softcorporation.sandi.base.service.solr.SolrService;
import com.softcorporation.sandi.base.util.Constants;
import com.softcorporation.sandi.index.model.TypeAheadEntry;
import com.softcorporation.sandi.index.service.TypeAheadIndexerService;

@Service
public class SolrTypeAheadIndexerService extends TypeAheadIndexerService {

    private static final Logger LOGGER = LoggerFactory.getLogger(SolrTypeAheadIndexerService.class);

    @Autowired
    private SolrService solrService;

    @Override
    protected Date getLastRunDate(String taCollection) {
        SolrQuery query = new SolrQuery(Constants._ID + ":\"" + LAST_RUN_DOC_ID + "\"");
        query.setFields(LAST_RUN_FIELD);
        query.setRows(1);
        try {
            QueryResponse response = solrService.getSolrClient().query(taCollection, query);
            SolrDocumentList results = response.getResults();
            if (!results.isEmpty()) {
                Object date = results.get(0).getFieldValue(LAST_RUN_FIELD);
                if (date instanceof Date) {
                    return (Date) date;
                }
            }
        } catch (Exception e) {
            LOGGER.warn("Failed to get last run date for '{}': {}", taCollection, e.getMessage());
        }
        return new Date(0);
    }

    @Override
    protected DocumentPage fetchDocuments(String clientId, String collection, String[] fields, Date createdAfter,
            int start, int rows) throws IOException {
        String dateFilter = SOLR_DATE_FORMAT.format(createdAfter);
        SolrQuery query = new SolrQuery("*:*");
        query.addFilterQuery(Constants.CLIENT_ID + ":" + clientId + " AND " + Constants.CREATED + ":[" + dateFilter + " TO *]");
        query.setFields(fields);
        query.setStart(start);
        query.setRows(rows);
        try {
            QueryResponse response = solrService.getSolrClient().query(collection, query);
            SolrDocumentList results = response.getResults();
            List<Map<String, Object>> docs = new ArrayList<>(results.size());
            for (SolrDocument solrDoc : results) {
                Map<String, Object> doc = new HashMap<>();
                for (String field : fields) {
                    Collection<Object> vals = solrDoc.getFieldValues(field);
                    if (vals != null) {
                        doc.put(field, new ArrayList<>(vals));
                    }
                }
                docs.add(doc);
            }
            return new DocumentPage(docs, results.getNumFound());
        } catch (SolrServerException e) {
            throw new IOException(e);
        }
    }

    @Override
    protected void flushToCollection(String taCollection, Map<String, TypeAheadEntry> map) throws IOException {
        List<String> texts = new ArrayList<>(map.keySet());

        for (int i = 0; i < texts.size(); i += BATCH_SIZE) {
            List<String> batch = texts.subList(i, Math.min(i + BATCH_SIZE, texts.size()));
            Map<String, Integer> existingCounts = queryExistingCounts(taCollection, batch);

            List<SolrInputDocument> solrDocs = new ArrayList<>(batch.size());
            for (String text : batch) {
                TypeAheadEntry entry = map.get(text);
                int totalCount = entry.getCount() + existingCounts.getOrDefault(text, 0);
                if (totalCount > 3) { // TODO
                    SolrInputDocument doc = new SolrInputDocument();
                    doc.addField(Constants._ID, text);
//                    doc.addField(TA_TEXT_FIELD, text);
                    doc.addField(TA_COUNT_FIELD, totalCount);
                    solrDocs.add(doc);
                }
            }
            if (solrDocs.size() > 0) {
                try {
                    solrService.getSolrClient().add(taCollection, solrDocs);
                } catch (SolrServerException e) {
                    throw new IOException(e);
                }
            }
        }
    }

    @Override
    protected void commit(String taCollection) throws IOException {
        try {
            solrService.getSolrClient().commit(taCollection);
        } catch (SolrServerException e) {
            throw new IOException(e);
        }
    }

    @Override
    protected void saveLastRunDate(String taCollection, Date date) throws IOException {
        SolrInputDocument doc = new SolrInputDocument();
        doc.addField(Constants._ID, LAST_RUN_DOC_ID);
        doc.addField(LAST_RUN_FIELD, date);
        try {
            solrService.getSolrClient().add(taCollection, doc);
        } catch (SolrServerException e) {
            throw new IOException(e);
        }
    }

    private Map<String, Integer> queryExistingCounts(String taCollection, List<String> texts) {
        Map<String, Integer> counts = new HashMap<>();
        if (texts.isEmpty()) return counts;

        StringBuilder sb = new StringBuilder();
        sb.append(Constants._ID).append(":(");
        for (int i = 0; i < texts.size(); i++) {
            if (i > 0) sb.append(" ");
            sb.append("\"").append(texts.get(i).replace("\"", "\\\"")).append("\"");
        }
        sb.append(")");

        SolrQuery query = new SolrQuery(sb.toString());
        query.setFields(Constants._ID, TA_COUNT_FIELD);
        query.setRows(texts.size());

        try {
            QueryResponse response = solrService.getSolrClient().query(taCollection, query);
            for (SolrDocument doc : response.getResults()) {
                String id = (String) doc.getFieldValue(Constants._ID);
                Object countVal = doc.getFieldValue(TA_COUNT_FIELD);
                if (id != null && countVal != null) {
                    counts.put(id, ((Number) countVal).intValue());
                }
            }
        } catch (Exception e) {
            LOGGER.warn("Failed to query existing type-ahead counts from '{}': {}", taCollection, e.getMessage());
        }
        return counts;
    }
}
