/**
 * Portal file storage security tests — S3-001/S3-002/S8-004.
 */
import { describe, it, expect, vi } from 'vitest'
import {
  buildPortalFileKey,
  buildAttachmentContentDisposition,
  createSignedDownloadUrl,
  portalFileKeyPrefix,
  validatePortalFileR2Key,
  validateUpload,
  PortalFileValidationError,
} from '../src/lib/portal-file-storage'

const TENANT = '11111111-1111-1111-1111-111111111111'
const CUSTOMER = '22222222-2222-2222-2222-222222222222'
const OTHER_TENANT = '33333333-3333-3333-3333-333333333333'

function validKey(filename = 'report.pdf'): string {
  return buildPortalFileKey(TENANT, CUSTOMER, filename)
}

describe('validatePortalFileR2Key', () => {
  it('accepts keys from buildPortalFileKey for the same tenant and customer', () => {
    const key = validKey()
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, key)).not.toThrow()
  })

  it('rejects keys outside tenant/customer prefix (staff route injection)', () => {
    const foreignKey = `${OTHER_TENANT}/portal/${CUSTOMER}/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa-stolen.pdf`
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, foreignKey)).toThrow(PortalFileValidationError)
    try {
      validatePortalFileR2Key(TENANT, CUSTOMER, foreignKey)
    } catch (err) {
      expect(err).toMatchObject({ code: 'INVALID_R2_KEY' })
    }
  })

  it('rejects keys for another customer within the same tenant', () => {
    const otherCustomer = '44444444-4444-4444-4444-444444444444'
    const key = buildPortalFileKey(TENANT, otherCustomer, 'secret.pdf')
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, key)).toThrow(PortalFileValidationError)
  })

  it('rejects path traversal segments', () => {
    const key = `${TENANT}/portal/${CUSTOMER}/../${OTHER_TENANT}/portal/x.pdf`
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, key)).toThrow(PortalFileValidationError)
  })

  it('rejects keys missing the uuid prefix segment', () => {
    const key = `${portalFileKeyPrefix(TENANT, CUSTOMER)}not-a-uuid-file.pdf`
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, key)).toThrow(PortalFileValidationError)
  })
})

describe('validateUpload MIME policy', () => {
  it('rejects text/html uploads', () => {
    expect(() =>
      validateUpload({ mime_type: 'text/html', file_size_bytes: 1024 }),
    ).toThrow(PortalFileValidationError)
    try {
      validateUpload({ mime_type: 'text/html', file_size_bytes: 1024 })
    } catch (err) {
      expect(err).toMatchObject({ code: 'MIME_NOT_ALLOWED' })
    }
  })

  it('rejects image/svg+xml uploads', () => {
    expect(() =>
      validateUpload({ mime_type: 'image/svg+xml', file_size_bytes: 1024 }),
    ).toThrow(PortalFileValidationError)
  })

  it('rejects MIME with charset parameters', () => {
    expect(() =>
      validateUpload({ mime_type: 'text/html; charset=utf-8', file_size_bytes: 1024 }),
    ).toThrow(PortalFileValidationError)
  })

  it('allows common document MIME types', () => {
    expect(() =>
      validateUpload({ mime_type: 'application/pdf', file_size_bytes: 1024 }),
    ).not.toThrow()
  })
})

describe('createSignedDownloadUrl', () => {
  it('includes attachment Content-Disposition in presigned GET URL', async () => {
    const key = validKey()
    const env = {
      STORAGE: {
        createPresignedUrl: vi.fn(),
      },
      CF_ACCOUNT_ID: 'acct',
      R2_ACCESS_KEY_ID: 'key',
      R2_SECRET_ACCESS_KEY: 'secret',
      R2_BUCKET_NAME: 'zync-storage',
    }

    const url = await createSignedDownloadUrl(env as never, key, { filename: 'report.pdf' })

    expect(url).toContain('response-content-disposition=')
    expect(decodeURIComponent(url)).toContain('attachment')
    expect(decodeURIComponent(url)).toContain('report.pdf')
  })
})

describe('buildAttachmentContentDisposition', () => {
  it('sanitizes header injection characters from filenames', () => {
    expect(buildAttachmentContentDisposition('evil"\r\nInjected.pdf')).toBe(
      'attachment; filename="evil___Injected.pdf"',
    )
  })
})

describe('portal create route validation gate', () => {
  it('staff create payload with foreign r2_key fails validation before DB', () => {
    const foreignKey = `${OTHER_TENANT}/expenses/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/receipt.pdf`
    expect(() => validatePortalFileR2Key(TENANT, CUSTOMER, foreignKey)).toThrow(PortalFileValidationError)
  })

  it('portal create payload with html mime fails validation before DB', () => {
    expect(() =>
      validateUpload({ mime_type: 'text/html', file_size_bytes: 100 }),
    ).toThrow(PortalFileValidationError)
  })
})
