import { describe, expect, it } from 'vitest'
import { canonicalStringifyOrganizationModel } from '../snapshot-hash'
import type { OrganizationModel } from '../types'
import { resolveOrganizationModel } from '../resolve'

// Minimal valid resolved model for hash-stability tests.
function makeModel(overrides: Partial<OrganizationModel> = {}): OrganizationModel {
  return resolveOrganizationModel(overrides)
}

describe('canonicalStringifyOrganizationModel', () => {
  it('produces the same string regardless of key insertion order', () => {
    // Two equivalent objects with different key ordering in domainMetadata.
    const model = makeModel()

    // Produce two copies with the same semantic content but different metadata key order.
    const a: OrganizationModel = { ...model }
    const b: OrganizationModel = {
      ...model,
      // Re-assign domainMetadata entries in a different key order.
      domainMetadata: Object.fromEntries(
        Object.entries(model.domainMetadata).reverse()
      ) as OrganizationModel['domainMetadata']
    }

    expect(canonicalStringifyOrganizationModel(a)).toBe(canonicalStringifyOrganizationModel(b))
  })

  it('excludes the snapshotHash field from the serialized output', () => {
    const base = makeModel()
    const withHash: OrganizationModel = { ...base, snapshotHash: 'abc123' }
    const withOtherHash: OrganizationModel = { ...base, snapshotHash: 'xyz789' }
    const withoutHash: OrganizationModel = { ...base, snapshotHash: undefined }

    // All three produce the same canonical string — snapshotHash is excluded.
    expect(canonicalStringifyOrganizationModel(withHash)).toBe(canonicalStringifyOrganizationModel(withoutHash))
    expect(canonicalStringifyOrganizationModel(withHash)).toBe(canonicalStringifyOrganizationModel(withOtherHash))
  })

  it('excludes domainMetadata lastModified from the serialized output', () => {
    const base = makeModel()

    // Two models differing only in lastModified.
    const earlyDate: OrganizationModel = {
      ...base,
      domainMetadata: {
        ...base.domainMetadata,
        branding: { version: 1, lastModified: '2026-01-01' }
      }
    }
    const lateDate: OrganizationModel = {
      ...base,
      domainMetadata: {
        ...base.domainMetadata,
        branding: { version: 1, lastModified: '2026-12-31' }
      }
    }

    expect(canonicalStringifyOrganizationModel(earlyDate)).toBe(canonicalStringifyOrganizationModel(lateDate))
  })

  it('produces different strings for different semantic content', () => {
    const modelA = makeModel({ version: 1 })
    const modelB = makeModel({
      version: 1,
      branding: { organizationName: 'AlphaOrg', productName: 'AlphaPlatform', shortName: 'Alpha' }
    })

    // Different semantic content → different canonical strings.
    expect(canonicalStringifyOrganizationModel(modelA)).not.toBe(canonicalStringifyOrganizationModel(modelB))
  })

  it('is deterministic across multiple calls with the same input', () => {
    const model = makeModel()
    const first = canonicalStringifyOrganizationModel(model)
    const second = canonicalStringifyOrganizationModel(model)
    const third = canonicalStringifyOrganizationModel(model)

    expect(first).toBe(second)
    expect(first).toBe(third)
  })
})
