import { describe, expect, it } from 'vitest'
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
import { ancestorsOf, childrenOf, topLevel } from '../helpers'
import { projectOrganizationSurfaces, validateOrganizationSurfaceProjection } from '../surface-projection'
import type {
  OrganizationModel,
  OrganizationModelSidebarNode,
  OrganizationModelSidebarSurfaceNode,
  OrganizationModelSystemEntry
} from '../types'

function makeSystem(id: string, overrides: Partial<OrganizationModelSystemEntry> = {}): OrganizationModelSystemEntry {
  return {
    id,
    order: 10,
    label: id,
    lifecycle: 'active',
    enabled: true,
    ...overrides
  }
}

function makeSurface(
  label: string,
  overrides: Partial<OrganizationModelSidebarSurfaceNode> = {}
): OrganizationModelSidebarSurfaceNode {
  return {
    type: 'surface',
    label,
    path: `/${label.toLowerCase().replaceAll(' ', '-')}`,
    surfaceType: 'page',
    order: 10,
    targets: {},
    ...overrides
  }
}

function makeModel(
  systems: OrganizationModelSystemEntry[],
  primary: Record<string, OrganizationModelSidebarNode>,
  bottom: Record<string, OrganizationModelSidebarNode> = {}
): OrganizationModel {
  return {
    ...DEFAULT_ORGANIZATION_MODEL,
    systems: Object.fromEntries(
      systems.map((system, index) => [system.id, { ...system, order: system.order ?? (index + 1) * 10 }])
    ),
    navigation: {
      sidebar: {
        primary,
        bottom
      }
    }
  }
}

describe('projectOrganizationSurfaces', () => {
  it('projects recursive sidebar surface leaves into data-only DTOs with inherited flags', () => {
    const model = makeModel(
      [
        makeSystem('sales', { requiresAdmin: true }),
        makeSystem('sales.crm', { parentSystemId: 'sales', devOnly: true })
      ],
      {
        business: {
          type: 'group',
          label: 'Business',
          order: 20,
          children: {
            'crm.pipeline': makeSurface('Pipeline', {
              path: '/crm/pipeline',
              surfaceType: 'graph',
              icon: 'crm',
              targets: {
                systems: ['sales.crm'],
                entities: ['crm.deal'],
                resources: ['workflow.crm-sync'],
                actions: ['crm.pipeline.manage']
              }
            })
          }
        }
      }
    )

    expect(projectOrganizationSurfaces(model)).toEqual([
      {
        id: 'crm.pipeline',
        label: 'Pipeline',
        path: '/crm/pipeline',
        surfaceType: 'graph',
        icon: 'crm',
        order: 10,
        systemIds: ['sales.crm'],
        entityIds: ['crm.deal'],
        resourceIds: ['workflow.crm-sync'],
        actionIds: ['crm.pipeline.manage'],
        enabled: true,
        devOnly: true,
        requiresAdmin: true
      }
    ])
  })

  it('sorts sidebar records by order before projecting leaves', () => {
    const model = makeModel([makeSystem('a'), makeSystem('b'), makeSystem('c')], {
      third: makeSurface('Third', { order: 30, targets: { systems: ['c'] } }),
      first: makeSurface('First', { order: 10, targets: { systems: ['a'] } }),
      second: makeSurface('Second', { order: 20, targets: { systems: ['b'] } })
    })

    expect(projectOrganizationSurfaces(model).map((surface) => surface.id)).toEqual(['first', 'second', 'third'])
  })

  it('uses canonical semantic system IDs directly', () => {
    const model = makeModel(
      [
        makeSystem('revenue'),
        makeSystem('revenue.crm'),
        makeSystem('revenue.lead-gen'),
        makeSystem('delivery'),
        makeSystem('delivery.projects')
      ],
      {
        'canonical.sales': makeSurface('Canonical Sales', {
          targets: { systems: ['revenue.crm', 'revenue.lead-gen', 'delivery.projects'] }
        })
      }
    )

    expect(projectOrganizationSurfaces(model)[0]).toMatchObject({
      systemIds: ['revenue.crm', 'revenue.lead-gen', 'delivery.projects']
    })
  })

  it('disables projected surfaces when system lineage is disabled', () => {
    const model = makeModel(
      [makeSystem('sales', { enabled: false }), makeSystem('sales.crm', { parentSystemId: 'sales' })],
      {
        'crm.pipeline': makeSurface('Pipeline', {
          targets: { systems: ['sales.crm'] }
        })
      }
    )

    expect(projectOrganizationSurfaces(model)[0]?.enabled).toBe(false)
  })
})

describe('validateOrganizationSurfaceProjection', () => {
  it('returns issue codes for duplicate sidebar leaf ids, duplicate paths, and unknown system targets', () => {
    const model = makeModel(
      [makeSystem('sales'), makeSystem('sales.crm')],
      {
        business: {
          type: 'group',
          label: 'Business',
          children: {
            'crm.pipeline': makeSurface('Pipeline', {
              path: '/crm/pipeline',
              targets: { systems: ['sales.crm'] }
            })
          }
        }
      },
      {
        'crm.pipeline': makeSurface('Pipeline Duplicate Id', {
          path: '/crm/pipeline/',
          targets: { systems: ['missing.related'] }
        })
      }
    )

    expect(validateOrganizationSurfaceProjection(model).map((issue) => issue.code)).toEqual([
      'duplicate-surface-id',
      'duplicate-surface-path',
      'unknown-surface-system'
    ])
  })

  it('emits unknown-surface-system with surfaceId and systemId for unrecognised system references', () => {
    const model = makeModel([makeSystem('exists')], {
      'test.surface': makeSurface('Test', {
        targets: { systems: ['exists', 'does.not.exist'] }
      })
    })

    const issues = validateOrganizationSurfaceProjection(model)
    expect(issues).toHaveLength(1)
    expect(issues[0]).toMatchObject({
      code: 'unknown-surface-system',
      surfaceId: 'test.surface',
      systemId: 'does.not.exist'
    })
  })
})

describe('projectOrganizationSurfaces - flag inheritance', () => {
  it('inherits requiresAdmin transitively from grandparent via parentSystemId chain', () => {
    const model = makeModel(
      [
        makeSystem('gp', { requiresAdmin: true }),
        makeSystem('gp.parent', { parentSystemId: 'gp' }),
        makeSystem('gp.parent.leaf', { parentSystemId: 'gp.parent' })
      ],
      { 'test.surface': makeSurface('Test', { targets: { systems: ['gp.parent.leaf'] } }) }
    )

    expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ requiresAdmin: true })
  })

  it('computes devOnly from lifecycle beta even when devOnly is not set', () => {
    const model = makeModel([makeSystem('beta-sys', { lifecycle: 'beta' })], {
      'test.surface': makeSurface('Test', { targets: { systems: ['beta-sys'] } })
    })

    expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ devOnly: true })
  })

  it('preserves explicit sidebar devOnly and requiresAdmin flags', () => {
    const model = makeModel([makeSystem('active-sys', { enabled: true, lifecycle: 'active' })], {
      'test.surface': makeSurface('Test', {
        devOnly: true,
        requiresAdmin: true,
        targets: { systems: ['active-sys'] }
      })
    })

    expect(projectOrganizationSurfaces(model)[0]).toMatchObject({ devOnly: true, requiresAdmin: true })
  })

  it('disables surface and preserves both systemIds when any referenced system is disabled', () => {
    const model = makeModel([makeSystem('sys-a', { enabled: true }), makeSystem('sys-b', { enabled: false })], {
      'test.surface': makeSurface('Test', { targets: { systems: ['sys-a', 'sys-b'] } })
    })

    const projection = projectOrganizationSurfaces(model)[0]
    expect(projection?.enabled).toBe(false)
    expect(projection?.systemIds).toEqual(['sys-a', 'sys-b'])
  })

  it('disables surface for deprecated lifecycle and archived lifecycle even when enabled is true', () => {
    for (const lifecycle of ['deprecated', 'archived'] as const) {
      const model = makeModel([makeSystem('gated-sys', { lifecycle, enabled: true })], {
        'test.surface': makeSurface('Test', { targets: { systems: ['gated-sys'] } })
      })

      expect(projectOrganizationSurfaces(model)[0]?.enabled, `lifecycle: ${lifecycle}`).toBe(false)
    }
  })

  it('filters unknown system references from projection systemIds', () => {
    const model = makeModel([makeSystem('exists')], {
      'test.surface': makeSurface('Test', { targets: { systems: ['exists', 'does.not.exist'] } })
    })

    expect(projectOrganizationSurfaces(model)[0]?.systemIds).toEqual(['exists'])
  })
})

describe('helpers - hierarchy traversal', () => {
  const systems: Record<string, OrganizationModelSystemEntry> = {
    a: makeSystem('a'),
    'a.b': makeSystem('a.b'),
    'a.b.c': makeSystem('a.b.c'),
    x: makeSystem('x')
  }

  it('ancestorsOf returns all path-segment ancestors including self in root-to-leaf order', () => {
    expect(ancestorsOf(systems, 'a.b.c').map((s) => s.id)).toEqual(['a', 'a.b', 'a.b.c'])
  })

  it('childrenOf returns only direct children, not grandchildren', () => {
    expect(childrenOf(systems, 'a').map((s) => s.id)).toEqual(['a.b'])
  })

  it('topLevel returns only root-level systems with no dot in their id', () => {
    expect(
      topLevel(systems)
        .map((s) => s.id)
        .sort()
    ).toEqual(['a', 'x'])
  })
})
