/**
 * 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.Date;
import java.util.List;

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.json.JSONArray;
import org.json.JSONObject;
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.service.IndexerService;
import com.softcorporation.sandi.index.util.IndexerException;

@Service
public class SolrIndexerService extends IndexerService {
    
    private static final Logger LOGGER = LoggerFactory.getLogger(SolrIndexerService.class);

    @Autowired
    private SolrService solrService;

    @Override
    public void indexDocument(final String collection, JSONObject document)
    		throws IndexerException {
        LOGGER.debug("Indexing JSON document to collection: {}", collection);
        
        try {
            SolrInputDocument solrDocument = convertJsonToSolrInputDocument(document);
            Date now = new Date();
            solrDocument.addField(Constants.INDEXED, now);
            if (!document.has(Constants.CREATED)) {
                solrDocument.addField(Constants.CREATED, now);
            }
            solrService.getSolrClient().add(collection, solrDocument);
            LOGGER.info("Successfully indexed 1 document to collection: {}", collection);
        } catch (Exception e) {
            LOGGER.error("Failed to index JSON document to collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }

    @Override
    public void indexDocuments(final String collection, List<JSONObject> documents)
    		throws IndexerException {
        LOGGER.debug("Starting indexing JSON documents to collection: {}", collection);
        
        List<SolrInputDocument> solrDocuments = new ArrayList<>();
        
        try {
            for (JSONObject doc : documents) {
                SolrInputDocument solrDoc = convertJsonToSolrInputDocument(doc);
                Date now = new Date();
                solrDoc.addField(Constants.INDEXED, now);
                if (!doc.has(Constants.CREATED)) {
                    solrDoc.addField(Constants.CREATED, now);
                }
                solrDocuments.add(solrDoc);
            }
            
            LOGGER.debug("Adding {} documents to Solr collection: {}", solrDocuments.size(), collection);
            solrService.getSolrClient().add(collection, solrDocuments);
            LOGGER.info("Successfully indexed {} documents to collection: {}", documents.size(), collection);
            
        } catch (Exception e) {
            LOGGER.error("Failed to index JSON document to collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }

    @Override
    public void deleteDocument(final String collection, String uniqueId) 
    		throws IndexerException {
        LOGGER.debug("Deleting document {} from collection: {}", uniqueId, collection);
        
        try {
            solrService.getSolrClient().deleteById(collection, uniqueId);
            LOGGER.info("Deleted document {} from collection: {}", uniqueId, collection);
        } catch (Exception e) {
            LOGGER.error("Failed to delete document from collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    @Override
    public void deleteDocument(final String collection, String clientId, String id)
    		throws IndexerException {
        LOGGER.debug("Deleting client {} document {} from collection: {}", clientId, id, collection);
        
        try {
        	String query = "_root_:\"" + clientId + "/"  + id + "\"";
            solrService.getSolrClient().deleteByQuery(collection, query);
            LOGGER.info("Deleted client {} document {} from collection: {}", clientId, id, collection);
        } catch (Exception e) {
            LOGGER.error("Failed to delete document from collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    @Override
    public void deleteDocuments(final String collection, List<String> uniqueIds) 
    		throws IndexerException {
        LOGGER.debug("Deleting documents from collection: {}", collection);
        
        try {
        	StringBuilder sb = new StringBuilder();
        	for (String uniqueId : uniqueIds) {
        		if (sb.length() > 0) {
        			sb.append(" ");
        		}
        		sb.append("\"" + uniqueId + "\"");
        	}
        	String query = "_id:(" + sb.toString() + ")";
            solrService.getSolrClient().deleteByQuery(collection, query);
            LOGGER.info("Deleted documents from collection: {}", collection);
        } catch (Exception e) {
            LOGGER.error("Failed to delete documents from collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    @Override
    public void deleteDocuments(final String collection, String clientId, List<String> ids) 
    		throws IndexerException {
        LOGGER.debug("Deleting client {} documents from collection: {}", clientId, collection);
        
        try {
        	StringBuilder sb = new StringBuilder();
        	for (String id : ids) {
        		if (sb.length() > 0) {
        			sb.append(" ");
        		}
        		sb.append("\"" + clientId + "/" + id + "\"");
        	}
        	String query = "_root_:(" + sb.toString() + ")";
            solrService.getSolrClient().deleteByQuery(collection, query);
            LOGGER.info("Deleted client {} documents from collection: {}", clientId, collection);
        } catch (Exception e) {
            LOGGER.error("Failed to delete client {} documents from collection {}: {}", clientId, collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    @Override
    public void deleteDocumentsByQuery(final String collection, final String clientId, final String query) 
    		throws IndexerException {
        LOGGER.debug("Deleting client {} documents from collection: {} by query: {}", clientId, collection, query);
        
        String deleteQuery = "_root_:" + clientId + "/* AND (" + query + ")";
        try {
            solrService.getSolrClient().deleteByQuery(collection, deleteQuery);
            LOGGER.info("Deleted client() documents from collection: {} by query: {}", clientId, collection, query);
        } catch (Exception e) {
            LOGGER.error("Failed to delete client {} documents from collection {}: by query {} {}", clientId, collection, query, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    public static JSONObject convertSolrDocumentToJson(SolrDocument solrDocument) {
        JSONObject jsonObject = new JSONObject();
        for (String fieldName : solrDocument.getFieldNames()) {
            Object fieldValue = solrDocument.getFieldValue(fieldName);
            jsonObject.put(fieldName, fieldValue);
        }
        return jsonObject;
    }
    
    public SolrInputDocument convertJsonToSolrInputDocument(JSONObject jsonObject) {
    	return convertJsonObject(jsonObject, "");
    }
    
    private SolrInputDocument convertJsonObject(JSONObject jsonObject, String path) {
        SolrInputDocument solrDoc = new SolrInputDocument();
//        solrDoc.addField(Constants.PATH, path.isEmpty() ? "/" : path);

        for (String key : jsonObject.keySet()) {
            Object value = jsonObject.get(key);
            
            if (value == null || value == JSONObject.NULL) {
                continue;
            } else if (value instanceof JSONObject) {
            	SolrInputDocument solrChild = convertJsonObject((JSONObject) value, path + "/" + key);
            	solrDoc.addField(key, solrChild);
            } else if (value instanceof JSONArray) {
            	List<Object> arrayValues = convertJsonArray((JSONArray) value, path + "/" + key);
            	solrDoc.addField(key, arrayValues);
            } else {
                solrDoc.addField(key, value);
            }
        }
        return solrDoc;
    }
    
    private List<Object> convertJsonArray(JSONArray jsonArray, String path) {
        List<Object> arrayValues = new ArrayList<>();
        
        for (int i = 0; i < jsonArray.length(); i++) {
            Object element = jsonArray.get(i);
            
            if (element == null || element == JSONObject.NULL) {
                continue;
            } else if (element instanceof JSONObject) {
            	arrayValues.add(convertJsonObject((JSONObject) element, path));
            } else if (element instanceof JSONArray) {
                arrayValues.add(convertJsonArray((JSONArray) element, path));
            } else {
                arrayValues.add(element);
            }
        }
        return arrayValues;
    }
    
    @Override
    public JSONObject getDocumentById(final String collection, final String id, final String fields) 
    		throws IndexerException {
        LOGGER.debug("Searching collection {} for unique ID: {}", collection, id);
        try {
            SolrQuery solrQuery = new SolrQuery();
           	solrQuery.setQuery("_id:\"" + id + "\"");
           	if (fields != null) {
               	solrQuery.setFields(fields);
           	}
           	solrQuery.setRows(1);
            QueryResponse response = solrService.getSolrClient().query(collection, solrQuery);
            SolrDocumentList solrResults = response.getResults();
            if (solrResults.isEmpty()) {
            	return null;
            }
            LOGGER.debug("Found existing document in collection {} with ID: {}", collection, id);
            SolrDocument solrDocument = solrResults.get(0);
            JSONObject json = convertSolrDocumentToJson(solrDocument);
            return json;
        } catch (Exception e) {
            LOGGER.error("Failed to find document in collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    @Override
    public JSONObject getDocumentById(final String collection, final String clientId, final String id, 
    		final String fields) throws IndexerException {
        LOGGER.debug("Searching collection {} for document ID: {}", collection, id);
        try {
            SolrQuery solrQuery = new SolrQuery();
           	solrQuery.setQuery("id:\"" + id + "\" AND _clientId:" + clientId);
           	if (fields != null) {
               	solrQuery.setFields(fields);
           	}
           	solrQuery.setRows(1);
            QueryResponse response = solrService.getSolrClient().query(collection, solrQuery);
            SolrDocumentList solrResults = response.getResults();
            if (solrResults.isEmpty()) {
            	return null;
            }
            LOGGER.debug("Found existing document in collection {} with ID: {}", collection, id);
            SolrDocument solrDocument = solrResults.get(0);
            JSONObject json = convertSolrDocumentToJson(solrDocument);
            return json;
        } catch (Exception e) {
            LOGGER.error("Failed to find document in collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
        }
    }
    
    public void commit(final String collection) throws IndexerException {
        LOGGER.debug("Committing changes to collection: {}", collection);
        try {
			solrService.getSolrClient().commit(collection);
		} catch (SolrServerException | IOException e) {
            LOGGER.error("Failed to commit changes to collection {}: {}", collection, e.getMessage(), e);
            throw new IndexerException(e.getMessage());
		}
   }
   
}