import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { defineDomainRecord } from '../helpers'
import { defineAction, defineActions, ActionSchema } from '../domains/actions'
import { defineEntity, defineEntities, EntitySchema } from '../domains/entities'
import { defineSystem, defineSystems, SystemEntrySchema } from '../domains/systems'
import { definePolicy, definePolicies, PolicySchema } from '../domains/policies'
import { defineRole, defineRoles, RoleSchema } from '../domains/roles'
import { defineGoal, defineGoals, ObjectiveSchema } from '../domains/goals'
import { defineCustomer, defineCustomers, CustomerSegmentSchema } from '../domains/customers'
import { defineOffering, defineOfferings, ProductSchema } from '../domains/offerings'
import { defineStatus, defineStatuses, StatusEntrySchema } from '../domains/statuses'
import { defineKnowledgeNode, defineKnowledgeNodes, OrgKnowledgeNodeSchema } from '../domains/knowledge'

// ---------------------------------------------------------------------------
// Minimal valid fixtures for each domain
// ---------------------------------------------------------------------------

const validAction: z.input<typeof ActionSchema> = {
  id: 'test-action',
  order: 10,
  label: 'Test Action',
  invocations: []
}

const validEntity: z.input<typeof EntitySchema> = {
  id: 'test.entity',
  order: 10,
  label: 'Test Entity',
  ownedBySystemId: 'test-system'
}

const validSystem: z.input<typeof SystemEntrySchema> = {
  id: 'test-system',
  order: 10,
  label: 'Test System'
}

const validPolicy: z.input<typeof PolicySchema> = {
  id: 'test-policy',
  order: 10,
  label: 'Test Policy',
  trigger: { kind: 'manual' },
  actions: [{ kind: 'block' }]
}

const validRole: z.input<typeof RoleSchema> = {
  id: 'test-role',
  order: 10,
  title: 'Test Role'
}

const validGoal: z.input<typeof ObjectiveSchema> = {
  id: 'test-goal',
  order: 10,
  description: 'Grow ARR by 2x',
  periodStart: '2026-01-01',
  periodEnd: '2026-12-31'
}

const validCustomer: z.input<typeof CustomerSegmentSchema> = {
  id: 'test-segment',
  order: 10,
  name: 'SMB Agencies'
}

const validOffering: z.input<typeof ProductSchema> = {
  id: 'test-product',
  order: 10,
  name: 'Starter Plan'
}

const validStatus: z.input<typeof StatusEntrySchema> = {
  id: 'test.status',
  order: 10,
  label: 'Test Status',
  semanticClass: 'delivery.task'
}

const validKnowledgeNode: z.input<typeof OrgKnowledgeNodeSchema> = {
  id: 'test-knowledge',
  kind: 'reference',
  title: 'Test Knowledge',
  summary: 'A test knowledge node.',
  body: 'Full body content here.',
  updatedAt: '2026-01-01'
}

// ---------------------------------------------------------------------------
// Generic defineDomainRecord
// ---------------------------------------------------------------------------

describe('defineDomainRecord', () => {
  it('produces an id-keyed map for valid entries', () => {
    const result = defineDomainRecord(ActionSchema, [validAction])
    expect(result).toHaveProperty('test-action')
    expect(result['test-action'].id).toBe('test-action')
    expect(result['test-action'].label).toBe('Test Action')
  })

  it('produces a map keyed by each entry id when given multiple entries', () => {
    const second: z.input<typeof ActionSchema> = { ...validAction, id: 'second-action', label: 'Second' }
    const result = defineDomainRecord(ActionSchema, [validAction, second])
    expect(Object.keys(result)).toEqual(['test-action', 'second-action'])
  })

  it('returns an empty map for an empty entries array', () => {
    const result = defineDomainRecord(ActionSchema, [])
    expect(result).toEqual({})
  })

  it('throws ZodError when an entry fails schema validation', () => {
    const bad = { id: 'bad', order: 'not-a-number', label: 'Bad' } as unknown as z.input<typeof ActionSchema>
    expect(() => defineDomainRecord(ActionSchema, [bad])).toThrow(z.ZodError)
  })

  it('applies schema defaults (invocations defaults to [])', () => {
    const minimal = { id: 'minimal-action', order: 10, label: 'Minimal' } as z.input<typeof ActionSchema>
    const result = defineDomainRecord(ActionSchema, [minimal])
    expect(result['minimal-action'].invocations).toEqual([])
  })
})

// ---------------------------------------------------------------------------
// Parameterized per-domain tests
// ---------------------------------------------------------------------------

describe.each([
  {
    domain: 'actions',
    defineSingle: defineAction,
    defineMultiple: defineActions,
    valid: validAction,
    schema: ActionSchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'entities',
    defineSingle: defineEntity,
    defineMultiple: defineEntities,
    valid: validEntity,
    schema: EntitySchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'systems',
    defineSingle: defineSystem,
    defineMultiple: defineSystems,
    valid: validSystem,
    schema: SystemEntrySchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'policies',
    defineSingle: definePolicy,
    defineMultiple: definePolicies,
    valid: validPolicy,
    schema: PolicySchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'roles',
    defineSingle: defineRole,
    defineMultiple: defineRoles,
    valid: validRole,
    schema: RoleSchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'goals',
    defineSingle: defineGoal,
    defineMultiple: defineGoals,
    valid: validGoal,
    schema: ObjectiveSchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'customers',
    defineSingle: defineCustomer,
    defineMultiple: defineCustomers,
    valid: validCustomer,
    schema: CustomerSegmentSchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'offerings',
    defineSingle: defineOffering,
    defineMultiple: defineOfferings,
    valid: validOffering,
    schema: ProductSchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'statuses',
    defineSingle: defineStatus,
    defineMultiple: defineStatuses,
    valid: validStatus,
    schema: StatusEntrySchema,
    idField: 'id' as const,
    invalidOverride: { order: 'not-a-number' }
  },
  {
    domain: 'knowledge',
    defineSingle: defineKnowledgeNode,
    defineMultiple: defineKnowledgeNodes,
    valid: validKnowledgeNode,
    schema: OrgKnowledgeNodeSchema,
    idField: 'id' as const,
    invalidOverride: { kind: 'invalid-kind' }
  }
])('define$domain — $domain', ({ domain: _domain, defineSingle, defineMultiple, valid, idField, invalidOverride }) => {
  it('defineX — validates and returns a parsed entry', () => {
    const result = defineSingle(valid as never)
    expect(result).toHaveProperty(idField)
    expect((result as Record<string, unknown>)[idField]).toBe((valid as Record<string, unknown>)[idField])
  })

  it('defineX — throws ZodError on invalid input', () => {
    const bad = { ...valid, ...invalidOverride } as never
    expect(() => defineSingle(bad)).toThrow(z.ZodError)
  })

  it('defineXs — produces an id-keyed map', () => {
    const result = defineMultiple([valid] as never[])
    const id = (valid as Record<string, unknown>)[idField] as string
    expect(result).toHaveProperty(id)
    expect((result[id] as Record<string, unknown>)[idField]).toBe(id)
  })

  it('defineXs — throws ZodError on invalid input', () => {
    const bad = { ...valid, ...invalidOverride } as never
    expect(() => defineMultiple([bad] as never[])).toThrow(z.ZodError)
  })

  it('defineXs — returns empty map for empty array', () => {
    const result = defineMultiple([] as never[])
    expect(result).toEqual({})
  })

  it('defineXs — validates through schema (no bypass)', () => {
    // Confirm that defineMultiple uses schema.parse, not a pass-through.
    // Missing required fields should throw even if the object looks similar.
    const missingRequired = { [idField]: (valid as Record<string, unknown>)[idField] } as never
    expect(() => defineMultiple([missingRequired] as never[])).toThrow(z.ZodError)
  })
})

// ---------------------------------------------------------------------------
// Topology — existing helpers (defineTopology, defineTopologyRelationship)
// are already tested elsewhere; verified here that they exist and are callable.
// ---------------------------------------------------------------------------

describe('topology — existing helpers', () => {
  it('defineTopologyRelationship is importable from domains/topology', async () => {
    const { defineTopologyRelationship, topologyRef } = await import('../domains/topology')
    const rel = defineTopologyRelationship({
      from: topologyRef.system('sales'),
      kind: 'uses',
      to: topologyRef.resource('lead-gen.company.qualify')
    })
    expect(rel.kind).toBe('uses')
    expect(rel.from.kind).toBe('system')
    expect(rel.to.kind).toBe('resource')
  })

  it('defineTopology is importable and produces a domain object', async () => {
    const { defineTopology, topologyRef } = await import('../domains/topology')
    const domain = defineTopology({
      'rel-1': {
        from: topologyRef.system('sales'),
        kind: 'triggers',
        to: topologyRef.resource('lead-gen.company.qualify')
      }
    })
    expect(domain.version).toBe(1)
    expect(domain.relationships).toHaveProperty('rel-1')
  })
})
