import { StateProjector } from '../state-projector'
import type { SDKState } from '../../types'

describe('StateProjector', () => {
  const projector = new StateProjector()

  // Helper: minimal SDKState
  function makeState(overrides: Partial<SDKState> = {}): SDKState {
    return {
      version: '1.0.0',
      lastModified: new Date(),
      tenantId: 'test',
      userId: 'user1',
      subscription: 'PROFESSIONAL',
      customerType: null,
      companyProfile: null,
      complianceScope: null,
      currentPhase: 1,
      currentStep: 'company-profile',
      completedSteps: [],
      checkpoints: {},
      importedDocuments: [],
      gapAnalysis: null,
      useCases: [],
      activeUseCase: null,
      screening: null,
      modules: [],
      requirements: [],
      controls: [],
      evidence: [],
      checklist: [],
      risks: [],
      aiActClassification: null,
      obligations: [],
      dsfa: null,
      toms: [],
      retentionPolicies: [],
      vvt: [],
      documents: [],
      cookieBanner: null,
      consents: [],
      dsrConfig: null,
      escalationWorkflows: [],
      preferences: {
        language: 'de',
        theme: 'light',
        compactMode: false,
        showHints: true,
        autoSave: true,
        autoValidate: true,
        allowParallelWork: true,
      },
      ...overrides,
    } as SDKState
  }

  function makeDecisionState(level: string = 'L2'): SDKState {
    return makeState({
      companyProfile: {
        companyName: 'Test GmbH',
        industry: 'IT-Dienstleistung',
        employeeCount: 50,
        businessModel: 'SaaS',
        isPublicSector: false,
      } as any,
      complianceScope: {
        decision: {
          id: 'dec-1',
          determinedLevel: level,
          scores: { risk_score: 60, complexity_score: 50, assurance_need: 55, composite_score: 55 },
          triggeredHardTriggers: [],
          requiredDocuments: [
            { documentType: 'vvt', label: 'VVT', required: true, depth: 'Standard', detailItems: ['Bezeichnung', 'Zweck'], estimatedEffort: '2h', triggeredBy: [] },
            { documentType: 'tom', label: 'TOM', required: true, depth: 'Standard', detailItems: ['Verschluesselung'], estimatedEffort: '3h', triggeredBy: [] },
            { documentType: 'lf', label: 'LF', required: true, depth: 'Basis', detailItems: [], estimatedEffort: '1h', triggeredBy: [] },
          ],
          riskFlags: [
            { id: 'rf-1', severity: 'MEDIUM', title: 'Cloud-Nutzung', description: '', recommendation: 'AVV pruefen' },
          ],
          gaps: [
            { id: 'gap-1', severity: 'high', title: 'TOM fehlt', description: 'Keine TOM definiert', relatedDocuments: ['tom'] },
          ],
          nextActions: [],
          reasoning: [],
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
        },
        answers: [],
      } as any,
      vvt: [{ id: 'vvt-1', name: 'Kundenverwaltung' }] as any[],
      toms: [],
      retentionPolicies: [],
    })
  }

  describe('projectForDraft', () => {
    it('should return a DraftContext with correct structure', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result).toHaveProperty('decisions')
      expect(result).toHaveProperty('companyProfile')
      expect(result).toHaveProperty('constraints')
      expect(result.decisions.level).toBe('L2')
    })

    it('should project company profile', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.companyProfile.name).toBe('Test GmbH')
      expect(result.companyProfile.industry).toBe('IT-Dienstleistung')
      expect(result.companyProfile.employeeCount).toBe(50)
    })

    it('should provide defaults when no company profile', () => {
      const state = makeState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.companyProfile.name).toBe('Unbekannt')
      expect(result.companyProfile.industry).toBe('Unbekannt')
      expect(result.companyProfile.employeeCount).toBe(0)
    })

    it('should extract constraints and depth requirements', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.constraints.depthRequirements).toBeDefined()
      expect(result.constraints.boundaries.length).toBeGreaterThan(0)
    })

    it('should extract risk flags', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.constraints.riskFlags.length).toBe(1)
      expect(result.constraints.riskFlags[0].title).toBe('Cloud-Nutzung')
    })

    it('should include existing document data when available', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.existingDocumentData).toBeDefined()
      expect((result.existingDocumentData as any).totalCount).toBe(1)
    })

    it('should return undefined existingDocumentData when none exists', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'tom')

      expect(result.existingDocumentData).toBeUndefined()
    })

    it('should filter required documents', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.decisions.requiredDocuments.length).toBe(3)
      expect(result.decisions.requiredDocuments.every(d => d.documentType)).toBe(true)
    })

    it('should handle empty state gracefully', () => {
      const state = makeState()
      const result = projector.projectForDraft(state, 'vvt')

      expect(result.decisions.level).toBe('L1')
      expect(result.decisions.hardTriggers).toEqual([])
      expect(result.decisions.requiredDocuments).toEqual([])
    })
  })

  describe('projectForAsk', () => {
    it('should return a GapContext with correct structure', () => {
      const state = makeDecisionState()
      const result = projector.projectForAsk(state)

      expect(result).toHaveProperty('unansweredQuestions')
      expect(result).toHaveProperty('gaps')
      expect(result).toHaveProperty('missingDocuments')
    })

    it('should identify missing documents', () => {
      const state = makeDecisionState()
      // vvt exists, tom and lf are missing
      const result = projector.projectForAsk(state)

      expect(result.missingDocuments.some(d => d.documentType === 'tom')).toBe(true)
      expect(result.missingDocuments.some(d => d.documentType === 'lf')).toBe(true)
    })

    it('should not list existing documents as missing', () => {
      const state = makeDecisionState()
      const result = projector.projectForAsk(state)

      // vvt exists in state
      expect(result.missingDocuments.some(d => d.documentType === 'vvt')).toBe(false)
    })

    it('should include gaps from scope decision', () => {
      const state = makeDecisionState()
      const result = projector.projectForAsk(state)

      expect(result.gaps.length).toBe(1)
      expect(result.gaps[0].title).toBe('TOM fehlt')
    })

    it('should handle empty state', () => {
      const state = makeState()
      const result = projector.projectForAsk(state)

      expect(result.gaps).toEqual([])
      expect(result.missingDocuments).toEqual([])
    })
  })

  describe('projectForValidate', () => {
    it('should return a ValidationContext with correct structure', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])

      expect(result).toHaveProperty('documents')
      expect(result).toHaveProperty('crossReferences')
      expect(result).toHaveProperty('scopeLevel')
      expect(result).toHaveProperty('depthRequirements')
    })

    it('should include all requested document types', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom'])

      expect(result.documents.length).toBe(2)
      expect(result.documents.map(d => d.type)).toContain('vvt')
      expect(result.documents.map(d => d.type)).toContain('tom')
    })

    it('should include cross-references', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])

      expect(result.crossReferences).toHaveProperty('vvtCategories')
      expect(result.crossReferences).toHaveProperty('tomControls')
      expect(result.crossReferences).toHaveProperty('retentionCategories')
      expect(result.crossReferences.vvtCategories.length).toBe(1)
      expect(result.crossReferences.vvtCategories[0]).toBe('Kundenverwaltung')
    })

    it('should include scope level', () => {
      const state = makeDecisionState('L3')
      const result = projector.projectForValidate(state, ['vvt'])

      expect(result.scopeLevel).toBe('L3')
    })

    it('should include depth requirements per document type', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom'])

      expect(result.depthRequirements).toHaveProperty('vvt')
      expect(result.depthRequirements).toHaveProperty('tom')
    })

    it('should summarize documents', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom'])

      expect(result.documents[0].contentSummary).toContain('1')
      expect(result.documents[1].contentSummary).toContain('Keine TOM')
    })

    it('should handle empty state', () => {
      const state = makeState()
      const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])

      expect(result.scopeLevel).toBe('L1')
      expect(result.crossReferences.vvtCategories).toEqual([])
      expect(result.crossReferences.tomControls).toEqual([])
    })
  })

  describe('token budget estimation', () => {
    it('projectForDraft should produce compact output', () => {
      const state = makeDecisionState()
      const result = projector.projectForDraft(state, 'vvt')
      const json = JSON.stringify(result)

      // Rough token estimation: ~4 chars per token
      const estimatedTokens = json.length / 4
      expect(estimatedTokens).toBeLessThan(2000) // Budget is ~1500
    })

    it('projectForAsk should produce very compact output', () => {
      const state = makeDecisionState()
      const result = projector.projectForAsk(state)
      const json = JSON.stringify(result)

      const estimatedTokens = json.length / 4
      expect(estimatedTokens).toBeLessThan(1000) // Budget is ~600
    })

    it('projectForValidate should stay within budget', () => {
      const state = makeDecisionState()
      const result = projector.projectForValidate(state, ['vvt', 'tom', 'lf'])
      const json = JSON.stringify(result)

      const estimatedTokens = json.length / 4
      expect(estimatedTokens).toBeLessThan(3000) // Budget is ~2000
    })
  })
})
