import test from 'node:test';
import assert from 'node:assert/strict';
import { createSeedWorkspace, addObject, addRepresentation, fieldRows, normalizeValue, setComparisonPolicy, verifyReceiptChain, applyReconciliation } from '../coherence/core.js';

async function workspace(values, canonical) {
  let w=await createSeedWorkspace();w=addObject(w,{label:'Record',externalKey:'test'}).workspace;
  for(const [index,value] of values.entries())w=addRepresentation(w,{system:`system-${index}`,recordKey:`record-${index}`,fields:{field:value},writable:true}).workspace;
  if(canonical!==undefined)w.objects.at(-1).canonical.field={value:canonical};
  return w;
}
const row=w=>fieldRows(w.objects.at(-1))[0];

test('identifiers preserve leading zeros and case by default',async()=>{
  assert.notEqual(normalizeValue('00123'),normalizeValue('123'));
  assert.notEqual(normalizeValue('Part-A'),normalizeValue('part-a'));
  assert.equal(row(await workspace(['00123','123'])).status,'DIFFERENT');
});

test('lossy number and object coercion is rejected at ingestion',async()=>{
  const w=await workspace(['original']);
  for(const value of [9007199254740992,1.25,{nested:'value'},['value']]) {
    assert.throws(()=>addRepresentation(w,{system:'new',recordKey:'new',fields:{field:value}}),/must be strings/);
  }
  assert.equal(normalizeValue('9007199254740993.000','decimal'),normalizeValue('9007199254740993','decimal'));
});

test('numeric equivalence is explicit and changing policy has a receipt',async()=>{
  const w=await workspace(['$1,240.00','1240']);
  assert.equal(row(w).status,'DIFFERENT');
  const changed=await setComparisonPolicy(w,{field:'field',policy:'usd',actor:'operator',reason:'This field is denominated in USD.'});
  assert.equal(row(changed.workspace).status,'SAME');
  assert.equal(changed.workspace.objects.at(-1).representations[0].fields.field,'$1,240.00');
  assert.equal(changed.receipt.action,'SET_COMPARISON_POLICY');
  assert.equal((await verifyReceiptChain(changed.workspace.receipts)).valid,true);
});

test('agreement among sources cannot hide disagreement with an assertion',async()=>{
  assert.equal(row(await workspace(['11','11'],'10')).status,'DIFFERENT');
});

test('malformed numeric grouping is unknown rather than false agreement',async()=>{
  const w=await workspace(['1,24','1,24']);
  const changed=await setComparisonPolicy(w,{field:'field',policy:'usd',actor:'operator',reason:'Dollar amount.'});
  assert.equal(row(changed.workspace).status,'UNKNOWN');
  assert.match(row(changed.workspace).comparisonError,/valid USD/);
  assert.throws(()=>normalizeValue('1,24','usd'));
  assert.throws(()=>normalizeValue('€124','usd'));
});

test('correction records its comparison policy and does not collapse numeric-looking IDs',async()=>{
  const w=await workspace(['00123','123']);
  const result=await applyReconciliation(w,{field:'field',targetValue:'00123',actor:'operator',reason:'The leading zeros are part of the identifier.'});
  assert.equal(result.plan[1].action,'UPDATE');
  assert.equal(result.receipt.comparisonPolicy,'exact');
  assert.equal(row(result.workspace).status,'SAME');
});
