import { describe, it, expect, vi, beforeEach } from 'vitest'
import { exportToPDF, exportToZIP, downloadExport } from '../export'
import type { SDKState } from '../types'

// Mock jsPDF as a class
vi.mock('jspdf', () => {
  return {
    default: class MockJsPDF {
      internal = {
        pageSize: { getWidth: () => 210, getHeight: () => 297 },
      }
      setFillColor = vi.fn().mockReturnThis()
      setDrawColor = vi.fn().mockReturnThis()
      setTextColor = vi.fn().mockReturnThis()
      setFontSize = vi.fn().mockReturnThis()
      setFont = vi.fn().mockReturnThis()
      setLineWidth = vi.fn().mockReturnThis()
      text = vi.fn().mockReturnThis()
      line = vi.fn().mockReturnThis()
      rect = vi.fn().mockReturnThis()
      roundedRect = vi.fn().mockReturnThis()
      circle = vi.fn().mockReturnThis()
      addPage = vi.fn().mockReturnThis()
      setPage = vi.fn().mockReturnThis()
      getNumberOfPages = vi.fn(() => 5)
      splitTextToSize = vi.fn((text: string) => [text])
      output = vi.fn(() => new Blob(['mock-pdf'], { type: 'application/pdf' }))
    },
  }
})

// Mock JSZip as a class
vi.mock('jszip', () => {
  return {
    default: class MockJSZip {
      private mockFolder = {
        file: vi.fn().mockReturnThis(),
        folder: vi.fn(() => this.mockFolder),
      }
      folder = vi.fn(() => this.mockFolder)
      generateAsync = vi.fn(() => Promise.resolve(new Blob(['mock-zip'], { type: 'application/zip' })))
    },
  }
})

const createMockState = (overrides: Partial<SDKState> = {}): SDKState => ({
  version: '1.0.0',
  lastModified: new Date('2024-01-15'),
  tenantId: 'test-tenant',
  userId: 'test-user',
  subscription: 'PROFESSIONAL',
  currentPhase: 1,
  currentStep: 'use-case-workshop',
  completedSteps: ['use-case-workshop', 'screening'],
  checkpoints: {
    'CP-UC': {
      checkpointId: 'CP-UC',
      passed: true,
      validatedAt: new Date(),
      validatedBy: 'SYSTEM',
      errors: [],
      warnings: [],
    },
  },
  useCases: [
    {
      id: 'uc-1',
      name: 'Test Use Case',
      description: 'A test use case for testing',
      category: 'Marketing',
      stepsCompleted: 3,
      steps: [
        { id: 's1', name: 'Step 1', completed: true, data: {} },
        { id: 's2', name: 'Step 2', completed: true, data: {} },
        { id: 's3', name: 'Step 3', completed: true, data: {} },
      ],
      assessmentResult: null,
      createdAt: new Date(),
      updatedAt: new Date(),
    },
  ],
  activeUseCase: 'uc-1',
  screening: null,
  modules: [],
  requirements: [],
  controls: [
    {
      id: 'ctrl-1',
      name: 'Test Control',
      description: 'A test control',
      type: 'TECHNICAL',
      category: 'Access Control',
      implementationStatus: 'IMPLEMENTED',
      effectiveness: 'HIGH',
      evidence: [],
      owner: 'Test Owner',
      dueDate: null,
    },
  ],
  evidence: [],
  checklist: [],
  risks: [
    {
      id: 'risk-1',
      title: 'Test Risk',
      description: 'A test risk',
      category: 'Security',
      likelihood: 3,
      impact: 4,
      severity: 'HIGH',
      inherentRiskScore: 12,
      residualRiskScore: 6,
      status: 'MITIGATED',
      mitigation: [
        {
          id: 'mit-1',
          description: 'Test mitigation',
          type: 'MITIGATE',
          status: 'COMPLETED',
          effectiveness: 80,
          controlId: 'ctrl-1',
        },
      ],
      owner: 'Risk Owner',
      relatedControls: ['ctrl-1'],
      relatedRequirements: [],
    },
  ],
  aiActClassification: null,
  obligations: [],
  dsfa: null,
  toms: [],
  retentionPolicies: [],
  vvt: [],
  documents: [],
  cookieBanner: null,
  consents: [],
  dsrConfig: null,
  escalationWorkflows: [],
  sbom: null,
  securityIssues: [],
  securityBacklog: [],
  commandBarHistory: [],
  recentSearches: [],
  preferences: {
    language: 'de',
    theme: 'light',
    compactMode: false,
    showHints: true,
    autoSave: true,
    autoValidate: true,
    allowParallelWork: true,
  },
  ...overrides,
} as unknown as SDKState)

describe('exportToPDF', () => {
  it('should return a Blob', async () => {
    const state = createMockState()
    const result = await exportToPDF(state)

    expect(result).toBeInstanceOf(Blob)
  })

  it('should create a PDF with the correct type', async () => {
    const state = createMockState()
    const result = await exportToPDF(state)

    expect(result.type).toBe('application/pdf')
  })

  it('should handle empty state', async () => {
    const emptyState = createMockState({
      useCases: [],
      risks: [],
      controls: [],
      completedSteps: [],
    })

    const result = await exportToPDF(emptyState)
    expect(result).toBeInstanceOf(Blob)
  })

  it('should handle state with multiple risks of different severities', async () => {
    const state = createMockState({
      risks: [
        {
          id: 'risk-1',
          title: 'Critical Risk',
          description: 'Critical',
          category: 'Security',
          likelihood: 5,
          impact: 5,
          severity: 'CRITICAL',
          inherentRiskScore: 25,
          residualRiskScore: 15,
          status: 'IDENTIFIED',
          mitigation: [],
          owner: null,
          relatedControls: [],
          relatedRequirements: [],
        },
        {
          id: 'risk-2',
          title: 'Low Risk',
          description: 'Low',
          category: 'Operational',
          likelihood: 1,
          impact: 1,
          severity: 'LOW',
          inherentRiskScore: 1,
          residualRiskScore: 1,
          status: 'ACCEPTED',
          mitigation: [],
          owner: null,
          relatedControls: [],
          relatedRequirements: [],
        },
      ],
    })

    const result = await exportToPDF(state)
    expect(result).toBeInstanceOf(Blob)
  })
})

describe('exportToZIP', () => {
  it('should return a Blob', async () => {
    const state = createMockState()
    const result = await exportToZIP(state)

    expect(result).toBeInstanceOf(Blob)
  })

  it('should create a ZIP with the correct type', async () => {
    const state = createMockState()
    const result = await exportToZIP(state)

    expect(result.type).toBe('application/zip')
  })

  it('should handle empty state', async () => {
    const emptyState = createMockState({
      useCases: [],
      risks: [],
      controls: [],
      completedSteps: [],
    })

    const result = await exportToZIP(emptyState)
    expect(result).toBeInstanceOf(Blob)
  })

  it('should respect includeEvidence option', async () => {
    const state = createMockState()
    const result = await exportToZIP(state, { includeEvidence: false })

    expect(result).toBeInstanceOf(Blob)
  })

  it('should respect includeDocuments option', async () => {
    const state = createMockState()
    const result = await exportToZIP(state, { includeDocuments: false })

    expect(result).toBeInstanceOf(Blob)
  })
})

describe('downloadExport', () => {
  let mockCreateElement: ReturnType<typeof vi.spyOn>
  let mockAppendChild: ReturnType<typeof vi.spyOn>
  let mockRemoveChild: ReturnType<typeof vi.spyOn>
  let mockLink: { href: string; download: string; click: ReturnType<typeof vi.fn> }

  beforeEach(() => {
    mockLink = {
      href: '',
      download: '',
      click: vi.fn(),
    }

    mockCreateElement = vi.spyOn(document, 'createElement').mockReturnValue(mockLink as unknown as HTMLElement)
    mockAppendChild = vi.spyOn(document.body, 'appendChild').mockImplementation(() => mockLink as unknown as HTMLElement)
    mockRemoveChild = vi.spyOn(document.body, 'removeChild').mockImplementation(() => mockLink as unknown as HTMLElement)
  })

  it('should download JSON format', async () => {
    const state = createMockState()
    await downloadExport(state, 'json')

    expect(mockLink.download).toContain('.json')
    expect(mockLink.click).toHaveBeenCalled()
  })

  it('should download PDF format', async () => {
    const state = createMockState()
    await downloadExport(state, 'pdf')

    expect(mockLink.download).toContain('.pdf')
    expect(mockLink.click).toHaveBeenCalled()
  })

  it('should download ZIP format', async () => {
    const state = createMockState()
    await downloadExport(state, 'zip')

    expect(mockLink.download).toContain('.zip')
    expect(mockLink.click).toHaveBeenCalled()
  })

  it('should include date in filename', async () => {
    const state = createMockState()
    await downloadExport(state, 'json')

    // Check that filename contains a date pattern
    expect(mockLink.download).toMatch(/ai-compliance-sdk-\d{4}-\d{2}-\d{2}\.json/)
  })

  it('should throw error for unknown format', async () => {
    const state = createMockState()

    await expect(downloadExport(state, 'unknown' as any)).rejects.toThrow('Unknown export format')
  })
})
