import { describe, expect, it } from 'vitest'
import { ZodError } from 'zod'
import {
  OmTopologyDomainSchema,
  OmTopologyMetadataSchema,
  OmTopologySystemInterfaceGrantMetadataSchema,
  OmTopologySystemInterfaceGrantSchema,
  defineTopology,
  defineTopologyRelationship,
  parseTopologyNodeRef,
  topologyRef
} from '../../domains/topology'
import { resolveOrganizationModel } from '../../resolve'

const VALID_SYSTEM = {
  id: 'sales',
  order: 10,
  label: 'Sales',
  enabled: true,
  lifecycle: 'active' as const
}

const SOURCE_RESOURCE = {
  id: 'email-discovery',
  order: 10,
  kind: 'workflow' as const,
  systemPath: 'sales',
  status: 'active' as const
}

const TARGET_RESOURCE = {
  id: 'email-verification',
  order: 20,
  kind: 'workflow' as const,
  systemPath: 'sales',
  status: 'active' as const
}

describe('OM topology domain', () => {
  it('accepts first-class topology relationships', () => {
    const model = resolveOrganizationModel({
      systems: { sales: VALID_SYSTEM },
      resources: {
        'email-discovery': SOURCE_RESOURCE,
        'email-verification': TARGET_RESOURCE
      },
      topology: {
        version: 1,
        relationships: {
          'email-discovery-triggers-email-verification': {
            from: { kind: 'resource', id: 'email-discovery' },
            kind: 'triggers',
            to: { kind: 'resource', id: 'email-verification' },
            systemPath: 'sales',
            required: true,
            metadata: { source: 'authored' }
          }
        }
      }
    })

    expect(model.topology.relationships['email-discovery-triggers-email-verification']).toMatchObject({
      from: { kind: 'resource', id: 'email-discovery' },
      kind: 'triggers',
      to: { kind: 'resource', id: 'email-verification' },
      required: true
    })
  })

  it('rejects dangling first-class topology refs', () => {
    expect(() =>
      resolveOrganizationModel({
        systems: { sales: VALID_SYSTEM },
        resources: { 'email-discovery': SOURCE_RESOURCE },
        topology: {
          version: 1,
          relationships: {
            'email-discovery-triggers-missing': {
              from: { kind: 'resource', id: 'email-discovery' },
              kind: 'triggers',
              to: { kind: 'resource', id: 'missing-resource' }
            }
          }
        }
      })
    ).toThrow(/unknown resource \\"missing-resource\\"/)
  })

  it('reports dangling topology refs on the relationship side path', () => {
    let error: unknown

    try {
      resolveOrganizationModel({
        systems: { sales: VALID_SYSTEM },
        resources: { 'email-discovery': SOURCE_RESOURCE },
        topology: {
          version: 1,
          relationships: {
            'email-discovery-triggers-missing': {
              from: { kind: 'resource', id: 'email-discovery' },
              kind: 'triggers',
              to: { kind: 'resource', id: 'missing-resource' }
            }
          }
        }
      })
    } catch (caught) {
      error = caught
    }

    expect(error).toBeInstanceOf(ZodError)
    if (!(error instanceof ZodError)) {
      throw new Error('Expected resolveOrganizationModel to throw a ZodError')
    }
    expect(error.issues).toEqual(
      expect.arrayContaining([
        expect.objectContaining({
          path: ['topology', 'relationships', 'email-discovery-triggers-missing', 'to']
        })
      ])
    )
  })

  it('rejects secret-like topology metadata', () => {
    expect(
      OmTopologyMetadataSchema.safeParse({
        owner: 'sales-ops',
        credentials: { apiKey: 'sk-test-12345678901234567890' }
      }).success
    ).toBe(false)
  })

  it('documents scoped System Interface topology grants', () => {
    const grant = OmTopologySystemInterfaceGrantSchema.parse({
      consumer: { systemPath: 'sales.lead-gen', interfaceKey: 'api' },
      provider: { systemPath: 'sales.crm', interfaceKey: 'api' },
      resourceIds: ['lead-gen-crm-handoff'],
      ontologyIds: ['sales.crm:object/deal']
    })

    expect(grant).toEqual({
      consumer: { systemPath: 'sales.lead-gen', interfaceKey: 'api' },
      provider: { systemPath: 'sales.crm', interfaceKey: 'api' },
      resourceIds: ['lead-gen-crm-handoff'],
      ontologyIds: ['sales.crm:object/deal']
    })
    expect(
      OmTopologySystemInterfaceGrantMetadataSchema.safeParse({
        systemInterfaceGrant: grant
      }).success
    ).toBe(true)
  })

  it('compiles typed resource objects through authoring helpers', () => {
    const relationship = defineTopologyRelationship({
      from: SOURCE_RESOURCE,
      kind: 'uses',
      to: topologyRef.externalResource('apollo'),
      required: true
    })

    expect(relationship).toEqual({
      from: { kind: 'resource', id: 'email-discovery' },
      kind: 'uses',
      to: { kind: 'externalResource', id: 'apollo' },
      required: true
    })

    expect(
      defineTopology({
        'email-discovery-uses-apollo': {
          from: SOURCE_RESOURCE,
          kind: 'uses',
          to: topologyRef.externalResource('apollo')
        }
      })
    ).toMatchObject({
      version: 1,
      relationships: {
        'email-discovery-uses-apollo': {
          from: { kind: 'resource', id: 'email-discovery' },
          to: { kind: 'externalResource', id: 'apollo' }
        }
      }
    })
  })

  it('parses boundary string refs without making raw strings canonical', () => {
    expect(parseTopologyNodeRef('resource:email-discovery')).toEqual({
      kind: 'resource',
      id: 'email-discovery'
    })
    expect(parseTopologyNodeRef('ontology:sales:action/contact.verify-email')).toEqual({
      kind: 'ontology',
      id: 'sales:action/contact.verify-email'
    })
    expect(() => parseTopologyNodeRef('email-discovery')).toThrow(/must use <kind>:<id>/)
    expect(() =>
      OmTopologyDomainSchema.parse({
        version: 1,
        relationships: {
          invalid: {
            from: 'resource:email-discovery',
            kind: 'uses',
            to: { kind: 'resource', id: 'email-verification' }
          }
        }
      })
    ).toThrow()
  })
})
