//
// Copyright (c) 2017-2026, Manticore Software LTD (https://manticoresearch.com)
// All rights reserved
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License. You should have
// received a copy of the GPL license along with this program; if you
// did not, you can find it at http://www.gnu.org/
//

/// @file _accumulator.h_
/// Declarations for the stuff related to accumulator


#ifndef _accumulator_
#define _accumulator_

#include "sphinxint.h"
#include "docstore.h"
#include "knnmisc.h"
#include "uuid_docid.h"

#include <functional>

struct StoredQueryDesc_t
{
	CSphFixedVector<CSphFilterSettings>	m_dFilters { 0 };
	CSphFixedVector<FilterTreeItem_t>	m_dFilterTree { 0 };

	CSphString						m_sQuery;
	CSphString						m_sTags;
	int64_t							m_iQUID = 0;
	bool							m_bQL = true;
};

class StoredQuery_i : public ISphNoncopyable, public StoredQueryDesc_t
{
public:
	virtual ~StoredQuery_i() {}
};

// commands that got replicated, transactions
enum class ReplCmd_e {
	PQUERY_ADD = 0,
	PQUERY_DELETE,
	TRUNCATE,
	CLUSTER_ALTER_ADD,
	CLUSTER_ALTER_DROP,
	RT_TRX,
	UPDATE_API,
	UPDATE_QL,
	UPDATE_JSON,
	AUTH_ADD,
	AUTH_DELETE,
	CLUSTER_ALTER_UPDATE_USER,

	TOTAL
};

struct CSphReconfigureSettings;
class InsertDocData_c;
struct RtSegment_t;
class MemoryWriter_c;

// command trait
struct ReplicationCommand_t
{
	// common
	ReplCmd_e m_eCommand { ReplCmd_e::TOTAL };
	WORD 					m_uVersion = 0;
	CSphString				m_sIndex; // move to accumulator
	CSphString				m_sCluster;
	int64_t					m_iClusterEpoch = 0;

	// add
	std::unique_ptr<StoredQuery_i> m_pStored;

	// delete
	CSphVector<int64_t>		m_dDeleteQueries;
	CSphString				m_sDeleteTags;
	StrVec_t				m_dUuidKeys;

	// truncate
	std::unique_ptr<CSphReconfigureSettings> m_tReconfigure;

	// commit related
	bool					m_bCheckIndex = true;
	bool					m_bIsolated = false;

	// update
	AttrUpdateSharedPtr_t m_pUpdateAPI;
	bool m_bBlobUpdate = false;
	const CSphQuery * m_pUpdateCond = nullptr;
};

struct ReplicatedCommand_t
{
	ReplCmd_e m_eCommand { ReplCmd_e::TOTAL };
	CSphString				m_sIndex;
	CSphString				m_sCluster;

	// index TID after operation for cluster binlog
	int64_t					m_iTID = -1;

	ReplicatedCommand_t & operator = ( const ReplicationCommand_t & tOther );
};

std::unique_ptr<ReplicationCommand_t> MakeReplicationCommand ( ReplCmd_e eCommand, CSphString sIndex, CSphString sCluster = CSphString() );

struct AttrWithModel_t
{
	knn::TextToEmbeddings_i *	m_pModel = nullptr;
	CSphVector<std::pair<int,bool>> m_dFrom;
};

class RtIndex_i;
class ColumnarBuilderRT_i;
class TableEmbeddings_c;

/// indexing accumulator
class RtAccum_t
{
public:
	~RtAccum_t();

	DWORD							m_uAccumDocs = 0;
	int64_t 						m_iAccumBytes = 0;
	CSphTightVector<CSphWordHit>	m_dAccum;
	CSphTightVector<CSphRowitem>	m_dAccumRows;
	CSphVector<DocID_t>				m_dAccumKlist;
	CSphTightVector<BYTE>			m_dBlobs;
	CSphVector<DWORD>				m_dPerDocHitsCount;
	CSphVector<std::unique_ptr<ReplicationCommand_t>> m_dCmd;
	ReplicatedCommand_t				m_tCmdReplicated;

	DictFormat_e				m_eDictFormat = DictFormat_e::CRC;
	DictRefPtr_c				m_pDict;
	const void *				m_pRefDict = nullptr; // not owned, used only for comparing via ==

public:
	bool			IsKeywordDict() const { return m_eDictFormat!=DictFormat_e::CRC; }
	void			SetupDict ( const RtIndex_i * pIndex, const DictRefPtr_c& pDict, DictFormat_e eDictFormat );
	void			Sort();
	bool			FetchEmbeddings ( TableEmbeddings_c * pEmbeddings, const CSphVector<AttrWithModel_t> & dAttrsWithModels, CSphString & sError );
	void			FetchEmbeddingsSrc ( InsertDocData_c & tDoc, const CSphVector<AttrWithModel_t> & dAttrsWithModels );

	void			CleanupPart();
	void			Cleanup();
	void			CleanReplicated();

	void			AddDocument ( ISphHits * pHits, const InsertDocData_c & tDoc, bool bReplace, int iRowSize, const DocstoreBuilder_i::Doc_t * pStoredDoc );
	void			CleanupDuplicates ( int iRowSize );
	void			ForEachUuidDocid ( const std::function<void ( ByteBlob_t )> & fnVisitor ) const;
	void			GrabLastWarning ( CSphString & sWarning );
	void			SetIndex ( RtIndex_i * pIndex );

	RowID_t			GenerateRowID();
	void			ResetRowID();
	void			BindUuidRegistry ( const UuidDocidRegistryPtr_t & pRegistry );
	bool			IsUuidRegistry ( const UuidDocidRegistry_i * pRegistry ) const;
	void			AdoptUuidLease ( const UuidDocidRegistry_i * pRegistry, const UuidDocidKey_t & tKey );
	void			ResetUuidLeases();
	uint64_t		GetSchemaHash() const { return m_uSchemaHash; }

	RtIndex_i *		GetIndex() const { return m_pIndex; }
	int 			GetIndexGeneration() const { return m_iIndexGeneration; }
	const CSphString & GetIndexName() const { return m_sIndexName; }
	int64_t			GetIndexId() const { return m_iIndexId; }

	ReplicationCommand_t * AddCommand ( ReplCmd_e eCmd, CSphString sIndex, CSphString sCluster = CSphString() );

	void			LoadRtTrx ( ByteBlob_t tTrx, DWORD uVer );
	void			SaveRtTrx ( MemoryWriter_c & tWriter ) const;

	const BYTE *	GetPackedKeywords() const;
	int				GetPackedLen() const;

	bool			SetupDocstore ( const RtIndex_i & tIndex, CSphString & sError );
	bool			IsReplace () const { return m_bReplace; }
	bool			IsPreparedForCommit () const noexcept { return m_bPreparedForCommit; }
	void			MarkPreparedForCommit () noexcept { m_bPreparedForCommit = true; }
	void			ResetPreparedForCommit () noexcept { m_bPreparedForCommit = false; }

	[[nodiscard]] bool IsClusterCommand () const noexcept;
	[[nodiscard]] bool IsRtTrxCommand () const noexcept;
	[[nodiscard]] bool IsUpdateCommand ( ) const noexcept;

private:
	bool								m_bReplace = false;		///< insert or replace mode (affects CleanupDuplicates() behavior)
	bool								m_bPreparedForCommit = false;

	ISphRtDictWraperRefPtr_c			m_pDictRt;
	std::unique_ptr<BlobRowBuilder_i>	m_pBlobWriter;
	std::unique_ptr<DocstoreRT_i>		m_pDocstore;
	std::unique_ptr<ColumnarBuilderRT_i> m_pColumnarBuilder;
	std::unique_ptr<EmbeddingsSrc_c>	m_pEmbeddingsSrc;
	RowID_t								m_tNextRowID = 0;
	CSphFixedVector<BYTE>				m_dPackedKeywords { 0 };
	uint64_t							m_uSchemaHash = 0;

	// FIXME!!! index is unlocked between add data and commit or at begin and end
	RtIndex_i *							m_pIndex = nullptr;		///< my current owner in this thread
	int									m_iIndexGeneration = 0;
	CSphString							m_sIndexName;
	int64_t								m_iIndexId = 0;
	UuidDocidRegistryPtr_t				m_pUuidRegistry;
	CSphVector<UuidDocidKey_t>			m_dUuidLeases;

	void			ResetDict();
	void			SetupDocstore();

	bool			RebuildStoragesForEmbeddings ( RowID_t tRowID, CSphRowitem * pRow, const CSphVector<AttrWithModel_t> & dAttrsWithModels, std::unique_ptr<BlobRowBuilder_i> & pNewBlobBuilder, std::unique_ptr<ColumnarBuilderRT_i> & pNewColumnarBuilder, std::unique_ptr<DocstoreRT_i> & pNewDocstoreBuilder, CSphVector<ScopedTypedIterator_t> & dAllIterators, const IntVec_t & dDocstoreRemap, const CSphColumnInfo * pBlobLoc, std::vector<std::vector<std::vector<float>>> & dAllEmbeddings, CSphVector<int64_t> & dTmp, CSphString & sError );
	bool			GenerateEmbeddings ( int iAttr, int iAttrWithModel, const CSphVector<AttrWithModel_t> & dAttrsWithModels, std::vector<std::vector<std::vector<float>>> & dAllEmbeddings, CSphString & sError );

	// defined in sphinxrt.cpp
	friend RtSegment_t* CreateSegment ( RtAccum_t*, int, ESphHitless, const VecTraits_T<SphWordID_t>&, CSphString& );
};

#endif // _accumulator_
