* mock_batch_anaf.prg - mock pentru calea BATCH de verificare ANAF (VerificareANAF.ANAF_SincronWebService_PlatitorTva,
* COMUN\programe\validare.prg:1865-2009), fara niciun apel HTTP real. Poveste S11 (docs\stories-cache-anaf.md).
*
* ALEGERE (functie vs strat HTTP): mock la nivel de METODA, prin subclasare - NU la nivelul stratului HTTP
* de dedesubt. Metoda originala foloseste Createobject('winHTTP.winHTTPrequest.5.1') / Createobject('Microsoft.XMLHTTP')
* cu ProgID literal, direct in validare.prg - nu exista nicio proprietate/metoda suprascriabila prin care sa
* injectez un obiect fals fara sa editez validare.prg, iar inlocuirea globala a inregistrarii COM (regsvr32/
* registry) ar afecta toata masina, nu doar acest test - inacceptabil ca efect secundar, deci exclusa. In schimb,
* VerificareANAF e o clasa VFP obisnuita (Define Class VerificareANAF As Custom, validare.prg:1814), deci o
* subclasa AICI (VerificareANAFMockBatch) suprascrie DOAR ANAF_SincronWebService_PlatitorTva si MOSTENESTE
* neschimbata ParseJsonANAFv8 (validare.prg:2015) - logica REALA de parsare (toate campurile crsANAF, inclusiv
* sursa/data_sursa adaugate de S3 la Create Cursor) ramane exercitata real; doar transportul HTTP e simulat.
* Grupurile de maxim 100 coduri sunt reproduse identic cu originalul (lnReqNo=100), fara INKEY(1) intre grupuri
* (asteptarea de 1 secunda din original nu are rost intr-un test headless).
*
* GAP pentru S5 (contractul 404 pe calea batch): subclasa suprascrie INTREGUL ANAF_SincronWebService_PlatitorTva,
* deci NU exercita codul real pe care S5 il va adauga/modifica in validare.prg (gate-ul "If loHTTP.Status = 200",
* validare.prg:1968). Pentru ca S5 sa-si testeze headless codul real, are nevoie de un punct de injectie in
* validare.prg pe care NU l-am facut (nu e fisierul meu) - vezi docs\diff-S11-mock-batch.md, sectiunea "Pentru S5".
* Scenariul HTTP404_CORP_COMPLET de mai jos arata AMBELE comportamente (azi: corpul e ignorat; propus: cu
* glANAFBatchIgnoraGate404 = .T., corpul se parseaza chiar si la status<>200) - fixture de referinta pentru S5.
*
* CONFIGURARE (construita INLINE in programul principal apelant, NU intr-o PROCEDURA/FUNCTION separata - vezi
* capcana l, COMUN\docs\depanare_testare_vfp.md: CREATEOBJECT/ADDPROPERTY apelate dintr-o procedura se comporta
* gresit in acest runtime headless; obiectele de configurare trebuie construite direct in main):
*   goANAFBatchMock            - Collection, cheie 'C'+CUI normalizat (NormalizeazaCUI din validare.prg), valoare:
*                                   - obiect Empty cu proprietati de raspuns (vezi lista mai jos) -> intra in "found"
*                                   - sirul 'NOTFOUND' -> intra explicit in "notFound"
*                                   - cheie lipsa din colectie -> tratat implicit tot ca 'NOTFOUND'
*                                   (simuleaza raspunsul ANAF pentru un CUI pe care nu-l cunoaste)
*   goANAFBatchScenariuPerGrup - Collection optionala, cheie TRANSFORM(nr. grup, 1-based), valoare = scenariu -
*                                   suprascrie gcANAFBatchScenariu DOAR pentru acel grup (test cu >100 coduri,
*                                   grupuri cu comportament diferit)
*   gcANAFBatchScenariu        - scenariul implicit pentru toate grupurile fara override in colectia de mai sus:
*                                   'NORMAL' (implicit daca lipseste) / 'TACERE' / 'RASPUNS_NEINTELES' /
*                                   'HTTP404_CORP_COMPLET'
*   glANAFBatchIgnoraGate404   - .F. implicit (comportamentul de AZI) - cu .T., scenariul HTTP404_CORP_COMPLET
*                                   parseaza corpul oricum (simuleaza fixul propus pentru S5)
*
* Proprietatile unui obiect de raspuns "gasit" (toate optionale, implicit gol/.F. cand lipsesc):
*   denumire, adresa, nrRegCom, data (C, 'YYYY-MM-DD')
*   scpTVA (L), dataInceputScpTVA, dataSfarsitScpTVA, dataAnulImpScpTVA (C 'YYYY-MM-DD'), mesajScpTVA (C)
*   inactiv (L), dataInactivare, dataReactivare, dataPublicare, dataRadiere (C 'YYYY-MM-DD')
*   tvaIncasare (L), dataInceputTvaInc, dataSfarsitTvaInc, dataActualizareTvaInc, dataPublicareTvaInc (C 'YYYY-MM-DD'), tipActTvaInc (C)
*   splitTva (L), dataInceputSplitTVA, dataAnulareSplitTVA (C 'YYYY-MM-DD')
* Datele sunt STRING 'YYYY-MM-DD' (acelasi format trimis de ANAF), nu Date VFP - fara ambiguitate legata de SET DATE.
* SENSIBILITATE LA DATA (adaugat 31.07.2026): daca perechea tcPropStart/tcPropEnd e prezenta pe item,
* PropBool evalueaza boolean-ul FATA de data ceruta (tdData, parametrul catre ANAF_SincronWebService_PlatitorTva),
* nu mai intoarce un boolean fix - la fel ca API-ul ANAF real (cere "data" per CUI, validare.prg:2098).
* Fara acele proprietati de data pe item, cade pe boolean-ul static de dinainte (compatibilitate cu testele
* existente care nu seteaza date). Mock-ul ramane marginit la O SINGURA perioada per camp (nu omniscient).
* Toate 4 campuri (scpTVA/tvaIncasare/splitTva/inactiv) folosesc BETWEEN inclusiv la ambele capete (adaugat
* 01.08.2026) - doc_WS_V9.txt (documentatia oficiala ANAF) nu precizeaza daca ziua dataReactivare insasi mai
* conteaza inactiva, deci mock-ul NU copiaza aici sfarsitul exclusiv folosit de cache pentru inactiv
* (validare.prg:2675) - acea margine ramane neverificata, vezi test_margine_inactiv_frontiera.prg.
*
* CONTOR DE APELURI (esential pentru S6 - codurile din cache nu trebuie sa ajunga la ANAF):
*   gnANAFBatchApeluri       - numeric, o unitate per GRUP efectiv trimis (indiferent de rezultat) - echivalentul
*                                unui loHTTP.Send() real
*   gcANAFBatchCuiuriTrimise - CSV, toate CUI-urile normalizate incluse efectiv intr-un grup trimis, in ordine -
*                                un CUI proaspat din cache NU trebuie sa apara niciodata aici
* Ambele se initializeaza (gnANAFBatchApeluri = 0, gcANAFBatchCuiuriTrimise = '') INLINE in programul principal,
* inainte de primul apel; mock-ul doar le incrementeaza/concateneaza (fara CREATEOBJECT, sigur din metoda de clasa).

Define Class VerificareANAFMockBatch As VerificareANAF

	Procedure ANAF_SincronWebService_PlatitorTva
		Lparameters tcCursorListaCIF, tdData, tcAlias
		Local lcAlias, lnRecords, lnReqNo, lnGrupuri, lnGrup, lnRec1, lnRec2
		Local lcCuiOriginal, lcCuiNorm, lcKey, loItem, lcScenariu, lcFound, lcNotFound, lcBody
		Local loJsonObject, loEx As Exception, llNumeric, lnI

		lcAlias = Iif(!Empty(m.tcAlias) And Type('tcAlias') = 'C', m.tcAlias, 'crsANAF')
		Use In (Select(m.lcAlias))

		lnRecords = Reccount(m.tcCursorListaCIF)
		If m.lnRecords = 0
			Return m.lcAlias
		Endif

		lnReqNo = 100
		lnGrupuri = Max(Ceiling(m.lnRecords / m.lnReqNo), 1)
		For lnGrup = 1 To m.lnGrupuri
			lnRec1 = m.lnReqNo * (m.lnGrup - 1) + 1
			lnRec2 = Min(m.lnReqNo * m.lnGrup, m.lnRecords)

			lcScenariu = This.ScenariuPentruGrup(m.lnGrup)
			lcFound = ''
			lcNotFound = ''

			Select (m.tcCursorListaCIF)
			Scan For Between(Recno(), m.lnRec1, m.lnRec2)
				lcCuiOriginal = Alltrim(cod_fiscal)
				lcCuiNorm = NormalizeazaCUI(m.lcCuiOriginal)

				llNumeric = !Empty(m.lcCuiNorm) And Len(m.lcCuiNorm) <= 10
				If m.llNumeric
					For lnI = 1 To Len(m.lcCuiNorm)
						If !Isdigit(Substr(m.lcCuiNorm, lnI, 1))
							llNumeric = .F.
							Exit
						Endif
					Endfor
				Endif
				If !m.llNumeric
					Loop
				Endif

				If Type('gnANAFBatchApeluri') = 'N'
					gcANAFBatchCuiuriTrimise = gcANAFBatchCuiuriTrimise + Iif(Empty(gcANAFBatchCuiuriTrimise), '', ',') + m.lcCuiNorm
				Endif

				lcKey = 'C' + m.lcCuiNorm
				If Type('goANAFBatchMock') = 'O' And goANAFBatchMock.GetKey(m.lcKey) > 0
					loItem = goANAFBatchMock.Item(m.lcKey)
				Else
					loItem = 'NOTFOUND'
				Endif
				If Vartype(m.loItem) = 'C'
					lcNotFound = lcNotFound + Iif(Empty(m.lcNotFound), '', ',') + m.lcCuiNorm
				Else
					lcFound = lcFound + Iif(Empty(m.lcFound), '', ',') + This.JsonRaspuns(m.loItem, m.lcCuiNorm, m.tdData)
				Endif
			Endscan

			If Type('gnANAFBatchApeluri') = 'N'
				gnANAFBatchApeluri = gnANAFBatchApeluri + 1
			Endif

			Do Case
			Case m.lcScenariu == 'TACERE' Or m.lcScenariu == 'RASPUNS_NEINTELES'
				* fara raspuns folosibil - grupul nu adauga niciun rand (ca la lcJsonString = '' in original)
			Case m.lcScenariu == 'HTTP404_CORP_COMPLET' And !This.lIgnoraGate404()
				* replica gate-ul de AZI (validare.prg:1968, "If loHTTP.Status = 200"): corpul exista dar e ignorat
			Otherwise
				* NORMAL, sau HTTP404_CORP_COMPLET cu glANAFBatchIgnoraGate404 = .T. (fixture pentru fixul propus S5)
				lcBody = '{"found":[' + m.lcFound + '],"notFound":[' + m.lcNotFound + ']}'
				Try
					loJsonObject = nfjsonread(m.lcBody)
					lcAlias = This.ParseJsonANAFv8(m.loJsonObject, m.tcAlias)
				Catch To loEx
					* pastreaza comportamentul original: o eroare de parsare nu opreste testul, doar grupul respectiv
				Endtry
			Endcase
		Endfor

		Return m.lcAlias
	Endproc && ANAF_SincronWebService_PlatitorTva

	*-- scenariul unui grup: intai override-ul per grup (goANAFBatchScenariuPerGrup), apoi implicitul global
	Procedure ScenariuPentruGrup
		Lparameters tnGrup
		Local lcCheie
		lcCheie = Transform(m.tnGrup)
		If Type('goANAFBatchScenariuPerGrup') = 'O' And goANAFBatchScenariuPerGrup.GetKey(m.lcCheie) > 0
			Return goANAFBatchScenariuPerGrup.Item(m.lcCheie)
		Endif
		If Type('gcANAFBatchScenariu') = 'C' And !Empty(gcANAFBatchScenariu)
			Return gcANAFBatchScenariu
		Endif
		Return 'NORMAL'
	Endproc

	Procedure lIgnoraGate404
		Return (Type('glANAFBatchIgnoraGate404') = 'L' And glANAFBatchIgnoraGate404)
	Endproc

	*-- serializeaza un raspuns "gasit" pentru un CUI, in forma JSON asteptata de ParseJsonANAFv8 (validare.prg:2015);
	*-- campurile lipsa din obiectul de configurare primesc valori implicite neutre (siruri goale / false).
	*!* 31.07.2026 marius.mutu: primeste tdData, il propaga la PropBool (booleanele devin sensibile la data ceruta)
	Procedure JsonRaspuns
		Lparameters toItem, tcCuiNorm, tdData
		Local lcJ
		lcJ = '{"date_generale":{'
		lcJ = lcJ + '"cui":' + Transform(Val(m.tcCuiNorm)) + ','
		lcJ = lcJ + '"data":"' + This.PropStr(m.toItem, 'data', '2024-01-01') + '",'
		lcJ = lcJ + '"denumire":"' + This.PropStr(m.toItem, 'denumire', 'FIRMA TEST ' + m.tcCuiNorm) + '",'
		lcJ = lcJ + '"adresa":"' + This.PropStr(m.toItem, 'adresa', 'ADRESA TEST') + '",'
		lcJ = lcJ + '"nrRegCom":"' + This.PropStr(m.toItem, 'nrRegCom', '') + '"'
		lcJ = lcJ + '},'
		lcJ = lcJ + '"inregistrare_scop_tva":{'
		lcJ = lcJ + '"scpTVA":' + This.PropBool(m.toItem, 'scpTVA', m.tdData, 'dataInceputScpTVA', 'dataSfarsitScpTVA') + ','
		lcJ = lcJ + '"perioade_tva":[{'
		lcJ = lcJ +   '"data_inceput_ScpTVA":"' + This.PropStr(m.toItem, 'dataInceputScpTVA', '') + '",'
		lcJ = lcJ +   '"data_sfarsit_ScpTVA":"' + This.PropStr(m.toItem, 'dataSfarsitScpTVA', '') + '",'
		lcJ = lcJ +   '"data_anul_imp_ScpTVA":"' + This.PropStr(m.toItem, 'dataAnulImpScpTVA', '') + '",'
		lcJ = lcJ +   '"mesaj_ScpTVA":"' + This.PropStr(m.toItem, 'mesajScpTVA', '') + '"'
		lcJ = lcJ + '}]'
		lcJ = lcJ + '},'
		lcJ = lcJ + '"inregistrare_RTVAI":{'
		lcJ = lcJ +   '"dataInceputTvaInc":"' + This.PropStr(m.toItem, 'dataInceputTvaInc', '') + '",'
		lcJ = lcJ +   '"dataSfarsitTvaInc":"' + This.PropStr(m.toItem, 'dataSfarsitTvaInc', '') + '",'
		lcJ = lcJ +   '"dataActualizareTvaInc":"' + This.PropStr(m.toItem, 'dataActualizareTvaInc', '') + '",'
		lcJ = lcJ +   '"dataPublicareTvaInc":"' + This.PropStr(m.toItem, 'dataPublicareTvaInc', '') + '",'
		lcJ = lcJ +   '"tipActTvaInc":"' + This.PropStr(m.toItem, 'tipActTvaInc', '') + '",'
		lcJ = lcJ +   '"statusTvaIncasare":' + This.PropBool(m.toItem, 'tvaIncasare', m.tdData, 'dataInceputTvaInc', 'dataSfarsitTvaInc')
		lcJ = lcJ + '},'
		lcJ = lcJ + '"stare_inactiv":{'
		lcJ = lcJ +   '"dataInactivare":"' + This.PropStr(m.toItem, 'dataInactivare', '') + '",'
		lcJ = lcJ +   '"dataReactivare":"' + This.PropStr(m.toItem, 'dataReactivare', '') + '",'
		lcJ = lcJ +   '"dataPublicare":"' + This.PropStr(m.toItem, 'dataPublicare', '') + '",'
		lcJ = lcJ +   '"dataRadiere":"' + This.PropStr(m.toItem, 'dataRadiere', '') + '",'
		lcJ = lcJ +   '"statusInactivi":' + This.PropBool(m.toItem, 'inactiv', m.tdData, 'dataInactivare', 'dataReactivare')
		lcJ = lcJ + '},'
		lcJ = lcJ + '"inregistrare_SplitTVA":{'
		lcJ = lcJ +   '"dataInceputSplitTVA":"' + This.PropStr(m.toItem, 'dataInceputSplitTVA', '') + '",'
		lcJ = lcJ +   '"dataAnulareSplitTVA":"' + This.PropStr(m.toItem, 'dataAnulareSplitTVA', '') + '",'
		lcJ = lcJ +   '"statusSplitTVA":' + This.PropBool(m.toItem, 'splitTva', m.tdData, 'dataInceputSplitTVA', 'dataAnulareSplitTVA')
		lcJ = lcJ + '}'
		lcJ = lcJ + '}'
		Return m.lcJ
	Endproc

	Procedure PropStr
		Lparameters toItem, tcProp, tcImplicit
		If Type('toItem.' + m.tcProp) <> 'U'
			Return Transform(Evaluate('toItem.' + m.tcProp))
		Endif
		Return m.tcImplicit
	Endproc

	*-- sensibil la data ceruta, ca serviciul real (ANAF trimite "data" per CUI, validare.prg:2098):
	*-- daca item are perechea tcPropStart/tcPropEnd (siruri 'YYYY-MM-DD'), evalueaza tdData IN acea
	*-- SINGURA perioada cunoscuta (mock-ul nu e omniscient, la fel ca ANAF v9 - o singura pereche
	*-- data_inceput/data_sfarsit, nu o lista, vezi docs\handoff_cache_anaf.md); fara acele
	*-- proprietati pe item, cade pe boolean-ul static dinainte (compatibil cu testele existente).
	*-- BETWEEN inclusiv pe toate 4 campurile (tlSfarsitExclusiv nefolosit la niciun apel): documentatia
	*-- oficiala ANAF (doc_WS_V9.txt) descrie fiecare boolean doar ca "true/false ... la data cautata",
	*-- fara sa precizeze daca ziua dataReactivare insasi mai conteaza inactiva - nu e o regula
	*-- documentata, deci nu se copiaza aici alegerea (exclusiva) facuta de cache in validare.prg:2675;
	*-- vezi test_margine_inactiv_frontiera.prg pentru cazul care ar evidentia diferenta, daca exista.
	*!* 01.08.2026 marius.mutu: PropBool nu mai copiaza sfarsitul exclusiv al cache-ului pentru inactiv (vezi docs\diff-S13-teste-echivalenta.md)
	Procedure PropBool
		Lparameters toItem, tcProp, tdData, tcPropStart, tcPropEnd, tlSfarsitExclusiv
		Local llStatic, ldStart, ldEnd, llExclusiv

		llStatic = (Vartype(m.tdData) <> 'D') Or Empty(m.tcPropStart) Or (Type('toItem.' + m.tcPropStart) = 'U')
		If m.llStatic
			If Type('toItem.' + m.tcProp) == 'L' And Evaluate('toItem.' + m.tcProp)
				Return 'true'
			Endif
			Return 'false'
		Endif

		llExclusiv = (Vartype(m.tlSfarsitExclusiv) == 'L' And m.tlSfarsitExclusiv)
		ldStart = This.ParseDataIso(Evaluate('toItem.' + m.tcPropStart))
		ldEnd = Iif(Empty(m.tcPropEnd) Or Type('toItem.' + m.tcPropEnd) = 'U', {}, This.ParseDataIso(Evaluate('toItem.' + m.tcPropEnd)))

		If Empty(m.ldStart)
			Return 'false'
		Endif
		If m.tdData >= m.ldStart And (Empty(m.ldEnd) Or Iif(m.llExclusiv, m.tdData < m.ldEnd, m.tdData <= m.ldEnd))
			Return 'true'
		Endif
		Return 'false'
	Endproc

	*-- 'YYYY-MM-DD' (formatul ANAF, vezi antet) -> Date VFP; sir gol/lipsa/invalid -> {} (data goala)
	Procedure ParseDataIso
		Lparameters tcIso
		If Vartype(m.tcIso) <> 'C' Or Len(Alltrim(m.tcIso)) <> 10
			Return {}
		Endif
		Return Date(Val(Substr(m.tcIso, 1, 4)), Val(Substr(m.tcIso, 6, 2)), Val(Substr(m.tcIso, 9, 2)))
	Endproc

Enddefine && VerificareANAFMockBatch
