
import fs from "node:fs"
import log from "baxxon/logger"
import TextDB from "baxxon/textdb"

export default async function( bx ){

/*
 * All the ways to interact with TextDB
 *
 *	1)	From a js module using setData, addRecord, updateRecord, removeRecord and write.
 *
 *	2)	From the file system by editing the file directly
 *		Problems with this method:
 *		changes to contents of file are made, so file system could get out of sync
 *		
 *	3)	From a UI by editing via htmldom
 *		Problems with this method:
 *		changes are made, but if write is never called, nothing is saved
*/

// reset test conditions
const testPath = bx.paths.site + "/data";
fs.rmSync( testPath, { recursive: true, force: true })
fs.mkdirSync( testPath );


class Tests {

	tests=[];
	count=0;
	passed=[];
	failed=[];

	add(test){
		this.tests.push(test);
	}

	run(){
		for(const test of this.tests){
			this.count++;
			switch(test.type){
				case "method":
				default:
					this.valMethod(test);
					break;
			}
		}
		log.note('Tests TOTAL', this.count, 'PASSED', this.passed.length, 'FAILED', this.failed.length);
	}

	valMethod(test){
		const normalize = (obj) => {
			if (Array.isArray(obj)) return obj.map(normalize);
			if (obj && typeof obj === 'object') {
				const newObj = {};
				Object.keys(obj)
					.filter(k => k !== 'dateCreated' && k !== 'dateModified')
					.sort()
					.forEach(k => {
						newObj[k] = normalize(obj[k]);
					});
				return newObj;
			}
			return obj;
		};

		const safeE=JSON.stringify(normalize(test.e));
		const safeR=JSON.stringify(normalize(test.r));
		const result=(safeE===safeR) ? 'PASS' : 'FAIL';
		let msg=`${test.m} [${result}]`;
		if(result==='FAIL'){
			msg+=` EXPECTED[${safeE}] RETURNED[${safeR}]`;
			this.failed.push(msg);
			//log.warn(msg);
		}else{
			this.passed.push(msg);
			//log.note(msg);
		}
	}
}

// tests
	
	const db = new TextDB({
		rootPath: bx.paths.site +"/data",
		fileFields: ["file"],
	})

	// m = message
	// r = result
	// e = expected
	let r;

	const tests = new Tests();

	// add record
	r=db.addRecord("a",{
		catg: "a",
		file: "b",
	},"Blob message");
	tests.add({ m:"Add record",r:r.success, e:true })
	tests.add({ m:"Add record - verify data", r: db.getData(),
		e:{"a/b":{"catg":"a","file":"b","_blob":"Blob message","id":"a/b"}},
	})

	// update record
	r=db.updateRecord("a/b",{
		catg: "AA",
		file: "b"
	},"New Blob message");
	tests.add({ m:"Update record",r:r.success, e:true });
	tests.add({ m:"Update record - verify data", r: db.getData(),
		e:{"a/b":{"catg":"AA","file":"b","_blob":"New Blob message","id":"a/b"}}
	});

	// remove record
	r=db.removeRecord("a/b");
	tests.add({ m:"Remove record",r:r.success, e:true });
	tests.add({ m:"Remove record - verify data", r: db.getData(), e:{}});


	// add record - diff directory
	r=db.addRecord("c",{
		catg: "a",
		file: "b",
	},"a diff message");
	tests.add({ m:"Add record diff dir",r:r.success, e:true });
	tests.add({ m:"Add record diff dir - verify data", r: db.getData(),
		e:{"c/b":{"catg":"a","file":"b","_blob":"a diff message","id":"c/b"}}
	});

	// update record & rename
	r=db.updateRecord("c/b",{
		catg: "c",
		file: "BB"
	});
	tests.add({ m:"Update record & rename",r:r.success, e:true });
	tests.add({ m:"Update record & rename - verify data", r: db.getData(),
		e:{"c/BB":{"catg":"c","file":"BB","_blob":"a diff message","id":"c/BB"}}
	});

	// after rename - add original record
	r=db.addRecord("c",{
		catg: "a",
		file: "b",
	},"a blank message")
	tests.add({ m:"Add renamed record back",r:r.success, e:true });
	tests.add({ m:"Add renamed record back - verify data", r: db.getData(),
		e:{
			"c/BB":{"catg":"c","file":"BB","_blob":"a diff message","id":"c/BB"},
			"c/b":{"catg":"a","file":"b","_blob":"a blank message","id":"c/b"}
		}
	});

	// clean up
	db.removeRecord("c/BB");
	db.removeRecord("c/b");


	// add record 
	db.addRecord("c",{
		catg: "a",
		file: "b",
	},"a blank message")

	// add same record
	r=db.addRecord("c",{
		catg: "a",
		file: "b",
	})
	tests.add({m:"Add record with existing id",r:r.success,e:false});
	db.removeRecord("c/b");

	// update record that doesn't exist
	r=db.updateRecord("c/x",{
		catg: "a",
		file: "x",
	})
	tests.add({m:"Update record that does not exist",r:r.success,e:false});

	// remover record that doesn't exist
	r=db.removeRecord("c/x",{
		catg: "a",
		file: "x",
	});
	tests.add({m:"Remove record that does not exist",r:r.success,e:false});

	// test bad fileField values

	r=db.addRecord("c",{
		catg: "a",
		file: "b/b/b",
	});
	tests.add({m:"Add record with bad id - using /",r:r.success,e:false});
	
	r=db.addRecord("c",{
		catg: "a",
		file: "BBxA Sing Song d",
	});
	tests.add({m:"Add record with mixed case id",r:r.success,e:true});
	tests.add({ m:"Get data for mixed case id", r: db.getData(),
		e: {"c/BBxASingSongd":{"catg":"a","file":"BBxA Sing Song d","_blob":"","id":"c/BBxASingSongd"}}
	});
	db.removeRecord("c/BBxASingSongd");
	
	// test strange Blob values
	// allow strings and convert numbers

	r=db.addRecord("b",{
		catg: "b",
		file: "c"
	},123);
	tests.add({m:"Blob test number",r:r.success,e:true});
	tests.add({ m:"Get data", r: db.getData(),
		e:{"b/c":{"catg":"b","file":"c","_blob":"123","id":"b/c"}}
	});
	db.removeRecord("b/c");

	// test strange meta values
	// allow strings and convert numbers
	r=db.addRecord("b",{
		catg: ["a","b","c"],
		obj: { an: "object" },
		bool: true,
		floatNum: 1.23,
		empty: "",
		file: "r"
	});
	tests.add({m:"metadata value is an array",r:r.success,e:true});
	tests.add({ m:"Get data", r: db.getData(),
		e:{"b/r":{"catg":"","obj":"","bool":"","floatNum":"1.23","empty":"","file":"r","_blob":"","id":"b/r"}}
	});
	db.removeRecord("b/r");


	// FS

	const db2 = new TextDB({
		rootPath: bx.paths.site +"/data",
		fileFields: ["file"],
		dirs: ["b"],
	})

	db2.addRecord("b",{
		catg: "b",
		file: "c"
	},"This is blob");
	await db2.write();
	tests.add({ m:"Get data after FS write", r: db2.getData(),
		e: {"b/c":{"catg":"b","file":"c","_blob":"This is blob","id":"b/c"}}
	});
	await db2.setData();
	tests.add({ m:"Get data after data reset", r: db2.getData(),
		e: {"b/c":{"_blob":"This is blob","catg":"b","file":"c","id":"b/c"}}
	});

	db2.removeRecord("b/c");
	await db2.write();
	tests.add({ m:"Get data after FS WRITE/delete", r: db2.getData(),
		e: {}
	});
	await db2.setData();
	tests.add({ m:"Get data after data reset", r: db2.getData(),
		e: {}
	});

	db2.addRecord("b",{
		catg: "b",
		file: "c"
	},"This is blob");
	db2.updateRecord("b/c",{
		catg: "b",
		file: "c",
		red: "blue",
		green: "orange"
	},"This is blob with even more text");
	await db2.write();
	tests.add({ m:"Get data after FS WRITE/update", r: db2.getData(),
		e: {"b/c":{"catg":"b","file":"c","_blob":"This is blob with even more text","id":"b/c","red":"blue","green":"orange"}}
	});
	await db2.setData();
	tests.add({ m:"Get data after data reset", r: db2.getData(),
		e: {"b/c":{"_blob":"This is blob with even more text","catg":"b","file":"c","red":"blue","green":"orange","id":"b/c"}}
	});

	// rename
	db2.updateRecord("b/c",{
		file: "d",
	});
	await db2.write();
	tests.add({ m:"Get data after FS WRITE/update & rename", r: db2.getData(),
		e:{"b/d":{"_blob":"This is blob with even more text","catg":"b","file":"d","red":"blue","green":"orange","id":"b/d"}}
	});
	await db2.setData();
	tests.add({ m:"Data reset after FS WRITE/update & rename", r: db2.getData(),
		e:{"b/d":{"_blob":"This is blob with even more text","catg":"b","file":"d","red":"blue","green":"orange","id":"b/d"}}
	});
	
	// Test setDataFromObject
	const newData = {
		"b/e": { file: "e", catg: "e", _blob: "New blob E" },
		"b/f": { file: "f", catg: "f", _blob: "New blob F" }
	};
	db2.setDataFromObject(newData);
	tests.add({ m:"setDataFromObject - verify internal state", r: db2.getData(),
		e: {
			"b/e": { file: "e", catg: "e", _blob: "New blob E", id: "b/e" },
			"b/f": { file: "f", catg: "f", _blob: "New blob F", id: "b/f" }
		}
	});
	await db2.write();
	await db2.setData();
	tests.add({ m:"setDataFromObject - verify FS persistence", r: db2.getData(),
		e: {
			"b/e": { file: "e", catg: "e", _blob: "New blob E", id: "b/e" },
			"b/f": { file: "f", catg: "f", _blob: "New blob F", id: "b/f" }
		}
	});
	
	tests.run();

	const txt =	"!!TextDB automated tests\n".concat(
		"TOTAL TESTS:" + tests.count,
		"\nPASSED " + tests.passed.length +"\n",
		tests.passed.join("\n"),
		"\nFAILED " + tests.failed.length +"\n",
		tests.failed.join("\n")
	);

	bx.render.TEXT(txt);
}
