import { describe, expect, it } from 'vitest'
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
import { buildOmCompilationContext, buildOmCrossRefIndex, knowledgeTargetExists } from '../cross-ref'
import { OrganizationModelSchema } from '../schema'
import type { OrganizationModel } from '../types'

// ---------------------------------------------------------------------------
// Test helpers
// ---------------------------------------------------------------------------

/**
 * Build a minimal but schema-valid OrganizationModel that contains:
 *  - a top-level system with id "sales" (path "sales")
 *  - a nested subsystem with id "sales.lead-gen" (path "sales.lead-gen")
 *  - a stage-kind catalog with id "sales.lead-gen:catalog/company-stage"
 *    and a single entry "prospect"
 *  - a knowledge node whose link targets the "prospect" stage
 */
function makeModelWithStage(): OrganizationModel {
  return {
    ...DEFAULT_ORGANIZATION_MODEL,
    systems: {
      sales: {
        id: 'sales',
        order: 10,
        label: 'Sales',
        enabled: true,
        lifecycle: 'active' as const,
        systems: {
          'lead-gen': {
            id: 'sales.lead-gen',
            order: 10,
            label: 'Lead Gen',
            enabled: true,
            lifecycle: 'active' as const,
            path: '/sales/lead-gen',
            ontology: {
              catalogTypes: {
                'sales.lead-gen:catalog/company-stage': {
                  id: 'sales.lead-gen:catalog/company-stage',
                  label: 'Company Stages',
                  ownerSystemId: 'sales.lead-gen',
                  kind: 'stage' as const,
                  entries: {
                    prospect: {
                      label: 'Prospect',
                      description: 'Company identified as a potential lead.',
                      order: 1
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    knowledge: {
      'knowledge.lead-gen-playbook': {
        id: 'knowledge.lead-gen-playbook',
        kind: 'playbook' as const,
        title: 'Lead Gen Playbook',
        summary: 'Governs the lead gen stage progression.',
        body: '## Overview\n\nPlaybook content.',
        links: [
          {
            target: { kind: 'stage' as const, id: 'prospect' },
            nodeId: 'stage:prospect'
          }
        ],
        ownerIds: [],
        updatedAt: '2026-05-17'
      }
    }
  }
}

// ---------------------------------------------------------------------------
// (a) stage-kind knowledge-link targets resolve via knowledgeTargetExists
// ---------------------------------------------------------------------------

describe('knowledgeTargetExists — stage kind', () => {
  it('buildOmCompilationContext returns a memoized cross-ref index plus ontology compilation', () => {
    const model = makeModelWithStage()
    const context = buildOmCompilationContext(model)

    expect(buildOmCompilationContext(model)).toBe(context)
    expect(context.crossRefIndex.stageIds.has('prospect')).toBe(true)
    expect(context.ontologyCompilation.ontology.catalogTypes['sales.lead-gen:catalog/company-stage']).toBeDefined()
    expect(buildOmCrossRefIndex(model)).toBe(context.crossRefIndex)
  })

  it('returns true for a stage id that exists in a stage-kind catalog', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    expect(knowledgeTargetExists(idx, 'stage', 'prospect')).toBe(true)
  })

  it('returns false for a stage id that does not exist in any catalog', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    expect(knowledgeTargetExists(idx, 'stage', 'nonexistent-stage')).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// (b) system targets resolve by dotted path AND by system.id (dual-keyed)
// ---------------------------------------------------------------------------

describe('knowledgeTargetExists — system kind dual-keyed lookup', () => {
  it('resolves a system by its dotted path', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    // "sales.lead-gen" is the dotted path in the systems tree
    expect(knowledgeTargetExists(idx, 'system', 'sales.lead-gen')).toBe(true)
  })

  it('resolves a system by system.id when id differs from tree key', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    // system.id === "sales.lead-gen" — same here, but the index is keyed by BOTH
    // the path AND the id so this also exercises the id branch
    expect(knowledgeTargetExists(idx, 'system', 'sales.lead-gen')).toBe(true)
  })

  it('returns false for an unknown system identifier', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    expect(knowledgeTargetExists(idx, 'system', 'sales.crm')).toBe(false)
  })
})

// ---------------------------------------------------------------------------
// (c) Schema path and verify path agree on stage-targeting knowledge node
// ---------------------------------------------------------------------------

describe('schema / verify path parity — stage target', () => {
  it('OrganizationModelSchema accepts a knowledge node with a stage link', () => {
    const model = makeModelWithStage()
    const result = OrganizationModelSchema.safeParse(model)
    const stageIssues = result.success
      ? []
      : result.error.issues.filter(
          (issue) =>
            issue.message.includes('stage') ||
            (Array.isArray(issue.path) && issue.path.includes('knowledge'))
        )
    expect(stageIssues).toHaveLength(0)
  })

  it('knowledgeTargetExists returns true for the stage target (verify path would accept)', () => {
    const model = makeModelWithStage()
    const idx = buildOmCrossRefIndex(model)
    const knowledgeNode = model.knowledge['knowledge.lead-gen-playbook']
    expect(knowledgeNode).toBeDefined()
    for (const link of knowledgeNode.links) {
      expect(knowledgeTargetExists(idx, link.target.kind, link.target.id)).toBe(true)
    }
  })

  it('schema and verify path agree: both accept the stage link without error', () => {
    const model = makeModelWithStage()

    // Schema path: parse must succeed (no issues about the stage link)
    const parseResult = OrganizationModelSchema.safeParse(model)
    const knowledgeIssues = parseResult.success
      ? []
      : parseResult.error.issues.filter((issue) => Array.isArray(issue.path) && issue.path[0] === 'knowledge')
    expect(knowledgeIssues).toHaveLength(0)

    // Verify path: knowledgeTargetExists must return true for every link
    const idx = buildOmCrossRefIndex(model)
    const node = model.knowledge['knowledge.lead-gen-playbook']
    const missingTargets = node.links.filter((link) => !knowledgeTargetExists(idx, link.target.kind, link.target.id))
    expect(missingTargets).toHaveLength(0)
  })
})
