import { describe, expect, it } from 'vitest'
import { authorizeContentAction, resolveRequiredContentCapabilities, assertCanManageTaxonomy, assertCanModify, assertCanPublish, ContentAuthorizationError, ContentAuthzError } from './authz.js'
import type { ResolvedContentType } from './registry.js'
import type { ResolvedContentStatus } from './status.js'

describe('content authz', () => {
  it('lets the author modify their own entry', () => {
    expect(() => assertCanModify({ id: 'u1' }, 'update', 'u1', 'e1')).not.toThrow()
  })

  it("lets canEditAny modify someone else's entry", () => {
    expect(() => assertCanModify({ id: 'u2', canEditAny: true }, 'update', 'u1', 'e1')).not.toThrow()
  })

  it('blocks a non-author without canEditAny, carrying action + actor + entry', () => {
    try {
      assertCanModify({ id: 'u2' }, 'remove', 'u1', 'e1')
      throw new Error('expected throw')
    } catch (e) {
      expect(e).toBeInstanceOf(ContentAuthzError)
      const err = e as ContentAuthzError
      expect(err.action).toBe('remove')
      expect(err.actorId).toBe('u2')
      expect(err.entryId).toBe('e1')
    }
  })

  it('gates state transitions on canPublish', () => {
    expect(() => assertCanPublish({ id: 'u1', canPublish: true }, 'publish', 'e1')).not.toThrow()
    expect(() => assertCanPublish({ id: 'u1' }, 'publish', 'e1')).toThrow(ContentAuthzError)
  })


  it('resolves ownership/status-sensitive capabilities from trusted entry metadata', () => {
    const type={capabilities:{
      manageType:'manage_type',migrateAll:'migrate_all',manageTerms:'manage_terms',create:'create',read:'read',readPrivate:'read_private',readProtected:'read_protected',
      editOwn:'edit_own',editOthers:'edit_others',editPrivate:'edit_private',editPublished:'edit_published',publish:'publish',
      deleteOwn:'delete_own',deleteOthers:'delete_others',deletePrivate:'delete_private',deletePublished:'delete_published',
    }} as ResolvedContentType
    const status={key:'published',published:true} as ResolvedContentStatus
    const principal={id:'u1',capabilities:new Set(['edit_own','edit_private','edit_published'])}
    const entry={id:'e1',author:'u1',status:'published',visibility:'private' as const}
    expect(resolveRequiredContentCapabilities({principal,type,status,operation:'edit',entry})).toEqual(['editOwn','editPrivate','editPublished'])
    expect(()=>authorizeContentAction({principal,type,status,operation:'edit',entry})).not.toThrow()
  })

  it('fails closed when status metadata is missing/mismatched or mapped capability is absent', () => {
    const type={capabilities:{
      manageType:'manage_type',migrateAll:'migrate_all',manageTerms:'manage_terms',create:'create',read:'read',readPrivate:'read_private',readProtected:'read_protected',
      editOwn:'edit_own',editOthers:'edit_others',editPrivate:'edit_private',editPublished:'edit_published',publish:'publish',
      deleteOwn:'delete_own',deleteOthers:'delete_others',deletePrivate:'delete_private',deletePublished:'delete_published',
    }} as ResolvedContentType
    const principal={id:'u1',capabilities:new Set<string>()}
    const entry={id:'e1',author:'u1',status:'published',visibility:'public' as const}
    expect(()=>authorizeContentAction({principal,type,operation:'edit',entry})).toThrow(ContentAuthorizationError)
    expect(()=>authorizeContentAction({principal,type,status:{key:'draft'} as ResolvedContentStatus,operation:'edit',entry})).toThrow(ContentAuthorizationError)
  })

  it('gates taxonomy management on canManageTaxonomy', () => {
    expect(() => assertCanManageTaxonomy({ id: 'u1', canManageTaxonomy: true })).not.toThrow()
    expect(() => assertCanManageTaxonomy({ id: 'u1' })).toThrow(ContentAuthzError)
  })
})
