import { describe, expect, it } from 'vitest'
import { z } from 'zod'
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
import { refineOrganizationModel } from '../schema-refinements'
import type { OrganizationModel } from '../types'

type RefinementCase = {
  name: string
  model: OrganizationModel
  expected: {
    message: string
    path: Array<string | number>
  }
}

function collectRefinementIssues(model: OrganizationModel): z.ZodIssue[] {
  const issues: z.ZodIssue[] = []
  const ctx = {
    addIssue(issue: z.ZodIssue): void {
      issues.push(issue)
    }
  } as z.RefinementCtx

  refineOrganizationModel(model, ctx)
  return issues
}

function makeModel(overrides: Partial<OrganizationModel>): OrganizationModel {
  return {
    ...DEFAULT_ORGANIZATION_MODEL,
    systems: {
      test: {
        id: 'test',
        order: 10,
        label: 'Test',
        lifecycle: 'active'
      }
    },
    ...overrides
  }
}

describe('refineOrganizationModel', () => {
  it('emits resource systemPath issues through the extracted refinement boundary', () => {
    const model: OrganizationModel = {
      ...DEFAULT_ORGANIZATION_MODEL,
      resources: {
        ...DEFAULT_ORGANIZATION_MODEL.resources,
        'missing-system-workflow': {
          id: 'missing-system-workflow',
          order: 10,
          kind: 'workflow',
          systemPath: 'missing.system',
          status: 'active'
        }
      }
    }

    const issues = collectRefinementIssues(model)

    expect(issues).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          code: z.ZodIssueCode.custom,
          path: ['resources', 'missing-system-workflow', 'systemPath'],
          message: 'Resource "missing-system-workflow" references unknown system path "missing.system"'
        })
      ])
    )
  })

  it('uses the shared ontology reference key map for loose ontology payload references', () => {
    const model: OrganizationModel = {
      ...DEFAULT_ORGANIZATION_MODEL,
      ontology: {
        ...DEFAULT_ORGANIZATION_MODEL.ontology,
        objectTypes: {
          ...DEFAULT_ORGANIZATION_MODEL.ontology.objectTypes,
          'global:object/test-object': {
            id: 'global:object/test-object',
            label: 'Test Object',
            properties: {
              badValueRef: { valueType: 'global:value-type/missing' }
            }
          }
        }
      }
    }

    const issues = collectRefinementIssues(model)

    expect(issues.some((issue) => issue.message.includes('valueType references unknown value-type ontology ID'))).toBe(
      true
    )
  })

  it.each<RefinementCase>([
    {
      name: 'system parent cycle',
      model: makeModel({
        systems: {
          a: { id: 'a', order: 10, label: 'A', parentSystemId: 'b' },
          b: { id: 'b', order: 20, label: 'B', parentSystemId: 'a' }
        }
      }),
      expected: {
        path: ['systems', 'a', 'parentSystemId'],
        message: 'System "a" has a parent cycle'
      }
    },
    {
      name: 'role reportsToId cycle',
      model: makeModel({
        roles: {
          'role.a': { id: 'role.a', order: 10, title: 'Role A', reportsToId: 'role.b' },
          'role.b': { id: 'role.b', order: 20, title: 'Role B', reportsToId: 'role.a' }
        }
      }),
      expected: {
        path: ['roles', 'role.a', 'reportsToId'],
        message: 'Role "role.a" has a reportsToId cycle'
      }
    },
    {
      name: 'duplicate sidebar paths',
      model: makeModel({
        navigation: {
          ...DEFAULT_ORGANIZATION_MODEL.navigation,
          sidebar: {
            primary: {
              first: { type: 'surface', label: 'First', path: '/shared', surfaceType: 'page', order: 10 },
              second: { type: 'surface', label: 'Second', path: '/shared/', surfaceType: 'page', order: 20 }
            },
            bottom: {}
          }
        }
      }),
      expected: {
        path: ['navigation', 'sidebar', 'primary', 'second', 'path'],
        message: 'Sidebar surface path "/shared/" duplicates surface "first"'
      }
    },
    {
      name: 'topology invalid refs',
      model: makeModel({
        topology: {
          version: 1,
          relationships: {
            missing: {
              from: { kind: 'system', id: 'missing' },
              kind: 'uses',
              to: { kind: 'system', id: 'test' }
            }
          }
        }
      }),
      expected: {
        path: ['topology', 'relationships', 'missing', 'from'],
        message: 'Topology relationship "missing" from references unknown system "missing"'
      }
    },
    {
      name: 'knowledge kind target mismatch',
      model: makeModel({
        roles: {
          'role.ops': { id: 'role.ops', order: 10, title: 'Ops' }
        },
        knowledge: {
          'knowledge.strategy': {
            id: 'knowledge.strategy',
            kind: 'strategy',
            title: 'Strategy',
            summary: 'Strategy summary',
            body: 'Body',
            updatedAt: '2026-06-04',
            ownerIds: [],
            links: [{ target: { kind: 'role', id: 'role.ops' }, nodeId: 'role:role.ops' }]
          }
        }
      }),
      expected: {
        path: ['knowledge', 'knowledge.strategy', 'links', 0, 'target', 'kind'],
        message: 'Knowledge node "knowledge.strategy" kind "strategy" cannot govern role targets'
      }
    },
    {
      name: 'entity unknown system',
      model: makeModel({
        entities: {
          lead: { id: 'lead', order: 10, label: 'Lead', ownedBySystemId: 'missing' }
        }
      }),
      expected: {
        path: ['entities', 'lead', 'ownedBySystemId'],
        message: 'Entity "lead" references unknown ownedBySystemId "missing"'
      }
    },
    {
      name: 'policy unknown action',
      model: makeModel({
        policies: {
          'policy.test': {
            id: 'policy.test',
            order: 10,
            label: 'Test Policy',
            trigger: { kind: 'action-invocation', actionId: 'missing.action' },
            actions: [{ kind: 'invoke-action', actionId: 'missing.action' }],
            appliesTo: { systemIds: [], actionIds: ['missing.action'], resourceIds: [], roleIds: [] }
          }
        }
      }),
      expected: {
        path: ['policies', 'policy.test', 'appliesTo', 'actionIds', 0],
        message: 'Policy "policy.test" applies to unknown action "missing.action"'
      }
    },
    {
      name: 'active system with no enabled descendants',
      model: makeModel({
        systems: {
          parent: {
            id: 'parent',
            order: 10,
            label: 'Parent',
            lifecycle: 'active',
            systems: {
              child: { id: 'child', order: 10, label: 'Child', lifecycle: 'archived' }
            }
          }
        }
      }),
      expected: {
        path: ['systems', 'parent', 'lifecycle'],
        message: 'System "parent" is active but has no active descendants'
      }
    }
  ])('emits focused refinement issues for $name', ({ model, expected }) => {
    const issues = collectRefinementIssues(model)

    expect(issues).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          code: z.ZodIssueCode.custom,
          path: expected.path,
          message: expected.message
        })
      ])
    )
  })
})
