/**
 * Compliance Client
 *
 * Main entry point for the SDK. Domain methods delegate to HttpTransport
 * for retry/timeout/abort handling. Transport primitives live in client-http.ts.
 */

import type {
  APIResponse,
  StateResponse,
  CheckpointValidationResult,
  AuthTokenRequest,
  AuthTokenResponse,
  RAGSearchRequest,
  RAGSearchResponse,
  RAGAskRequest,
  RAGAskResponse,
  ExportFormat,
  SDKState,
  CheckpointStatus,
} from '@breakpilot/compliance-sdk-types'
import {
  HttpTransport,
  createHttpError,
  type APIError,
} from './client-http'

// =============================================================================
// TYPES
// =============================================================================

export interface ComplianceClientOptions {
  apiEndpoint: string
  apiKey?: string
  tenantId: string
  timeout?: number
  maxRetries?: number
  onError?: (error: Error) => void
  onAuthError?: () => void
}

// =============================================================================
// COMPLIANCE CLIENT
// =============================================================================

export class ComplianceClient {
  private http: HttpTransport

  constructor(options: ComplianceClientOptions) {
    this.http = new HttpTransport({
      apiEndpoint: options.apiEndpoint,
      tenantId: options.tenantId,
      timeout: options.timeout,
      maxRetries: options.maxRetries,
      onError: options.onError,
      onAuthError: options.onAuthError,
    })
    if (options.apiKey) {
      this.http.setApiKey(options.apiKey)
    }
  }

  private get apiEndpoint(): string {
    return this.http.apiEndpoint
  }

  private get tenantId(): string {
    return this.http.getTenantId()
  }

  // ---------------------------------------------------------------------------
  // Authentication
  // ---------------------------------------------------------------------------

  async authenticate(request: AuthTokenRequest): Promise<AuthTokenResponse> {
    const response = await this.http.fetchWithRetry<APIResponse<AuthTokenResponse>>(
      `${this.apiEndpoint}/auth/token`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request),
      }
    )

    if (response.success && response.data) {
      this.http.setAccessToken(response.data.accessToken)
      return response.data
    }

    throw createHttpError(response.error || 'Authentication failed', 401, false)
  }

  async refreshToken(refreshToken: string): Promise<AuthTokenResponse> {
    return this.authenticate({
      grantType: 'refresh_token',
      clientId: '',
      refreshToken,
    })
  }

  setAccessToken(token: string): void {
    this.http.setAccessToken(token)
  }

  clearAccessToken(): void {
    this.http.setAccessToken(null)
  }

  // ---------------------------------------------------------------------------
  // State Management
  // ---------------------------------------------------------------------------

  async getState(): Promise<StateResponse | null> {
    try {
      const response = await this.http.fetchWithRetry<APIResponse<StateResponse>>(
        `${this.apiEndpoint}/state?tenantId=${encodeURIComponent(this.tenantId)}`,
        {
          method: 'GET',
          headers: this.http.getHeaders(),
        }
      )

      if (response.success && response.data) {
        return response.data
      }

      return null
    } catch (error) {
      const apiError = error as APIError
      if (apiError.status === 404) {
        return null
      }
      throw error
    }
  }

  async saveState(state: SDKState, version?: number): Promise<StateResponse> {
    const response = await this.http.fetchWithRetry<APIResponse<StateResponse>>(
      `${this.apiEndpoint}/state`,
      {
        method: 'POST',
        headers: {
          ...this.http.getHeaders(),
          ...(version !== undefined && { 'If-Match': String(version) }),
        },
        body: JSON.stringify({
          tenantId: this.tenantId,
          state,
          version,
        }),
      }
    )

    if (!response.success) {
      throw createHttpError(response.error || 'Failed to save state', 500, true)
    }

    return response.data!
  }

  async deleteState(): Promise<void> {
    await this.http.fetchWithRetry<APIResponse<void>>(
      `${this.apiEndpoint}/state?tenantId=${encodeURIComponent(this.tenantId)}`,
      {
        method: 'DELETE',
        headers: this.http.getHeaders(),
      }
    )
  }

  // ---------------------------------------------------------------------------
  // Checkpoints
  // ---------------------------------------------------------------------------

  async validateCheckpoint(
    checkpointId: string,
    data?: unknown
  ): Promise<CheckpointValidationResult> {
    const response = await this.http.fetchWithRetry<
      APIResponse<CheckpointValidationResult>
    >(`${this.apiEndpoint}/checkpoints/validate`, {
      method: 'POST',
      headers: this.http.getHeaders(),
      body: JSON.stringify({
        tenantId: this.tenantId,
        checkpointId,
        data,
      }),
    })

    if (!response.success || !response.data) {
      throw createHttpError(response.error || 'Checkpoint validation failed', 500, true)
    }

    return response.data
  }

  async getCheckpoints(): Promise<Record<string, CheckpointStatus>> {
    const response = await this.http.fetchWithRetry<
      APIResponse<Record<string, CheckpointStatus>>
    >(`${this.apiEndpoint}/checkpoints?tenantId=${encodeURIComponent(this.tenantId)}`, {
      method: 'GET',
      headers: this.http.getHeaders(),
    })

    return response.data || {}
  }

  // ---------------------------------------------------------------------------
  // RAG
  // ---------------------------------------------------------------------------

  async searchRAG(request: RAGSearchRequest): Promise<RAGSearchResponse> {
    const response = await this.http.fetchWithRetry<APIResponse<RAGSearchResponse>>(
      `${this.apiEndpoint}/rag/search`,
      {
        method: 'POST',
        headers: this.http.getHeaders(),
        body: JSON.stringify(request),
      }
    )

    if (!response.success || !response.data) {
      throw createHttpError(response.error || 'RAG search failed', 500, true)
    }

    return response.data
  }

  async askRAG(request: RAGAskRequest): Promise<RAGAskResponse> {
    const response = await this.http.fetchWithRetry<APIResponse<RAGAskResponse>>(
      `${this.apiEndpoint}/rag/ask`,
      {
        method: 'POST',
        headers: this.http.getHeaders(),
        body: JSON.stringify(request),
      }
    )

    if (!response.success || !response.data) {
      throw createHttpError(response.error || 'RAG query failed', 500, true)
    }

    return response.data
  }

  // ---------------------------------------------------------------------------
  // Export
  // ---------------------------------------------------------------------------

  async exportState(format: ExportFormat): Promise<Blob> {
    const response = await this.http.fetchWithTimeout(
      `${this.apiEndpoint}/export?tenantId=${encodeURIComponent(this.tenantId)}&format=${format}`,
      {
        method: 'GET',
        headers: {
          ...this.http.getHeaders(),
          Accept:
            format === 'json'
              ? 'application/json'
              : format === 'pdf'
                ? 'application/pdf'
                : 'application/octet-stream',
        },
      },
      `export-${Date.now()}`
    )

    if (!response.ok) {
      throw createHttpError(`Export failed: ${response.statusText}`, response.status, true)
    }

    return response.blob()
  }

  // ---------------------------------------------------------------------------
  // Document Generation
  // ---------------------------------------------------------------------------

  async generateDocument(
    type: 'dsfa' | 'tom' | 'vvt' | 'gutachten' | 'privacy_policy' | 'cookie_banner',
    options?: Record<string, unknown>
  ): Promise<{ id: string; status: string; content?: string }> {
    const response = await this.http.fetchWithRetry<
      APIResponse<{ id: string; status: string; content?: string }>
    >(`${this.apiEndpoint}/generate/${type}`, {
      method: 'POST',
      headers: this.http.getHeaders(),
      body: JSON.stringify({
        tenantId: this.tenantId,
        options,
      }),
    })

    if (!response.success || !response.data) {
      throw createHttpError(response.error || 'Document generation failed', 500, true)
    }

    return response.data
  }

  // ---------------------------------------------------------------------------
  // Security Scan
  // ---------------------------------------------------------------------------

  async startSecurityScan(options?: {
    tools?: string[]
    targetPath?: string
    severityThreshold?: string
    generateSBOM?: boolean
  }): Promise<{ id: string; status: string }> {
    const response = await this.http.fetchWithRetry<
      APIResponse<{ id: string; status: string }>
    >(`${this.apiEndpoint}/security/scan`, {
      method: 'POST',
      headers: this.http.getHeaders(),
      body: JSON.stringify({
        tenantId: this.tenantId,
        ...options,
      }),
    })

    if (!response.success || !response.data) {
      throw createHttpError(response.error || 'Security scan failed', 500, true)
    }

    return response.data
  }

  async getSecurityScanResult(scanId: string): Promise<unknown> {
    const response = await this.http.fetchWithRetry<APIResponse<unknown>>(
      `${this.apiEndpoint}/security/scan/${scanId}`,
      {
        method: 'GET',
        headers: this.http.getHeaders(),
      }
    )

    return response.data
  }

  // ---------------------------------------------------------------------------
  // Utility
  // ---------------------------------------------------------------------------

  cancelAllRequests(): void {
    this.http.cancelAllRequests()
  }

  setTenantId(tenantId: string): void {
    this.http.setTenantId(tenantId)
  }

  getTenantId(): string {
    return this.http.getTenantId()
  }

  async healthCheck(): Promise<boolean> {
    try {
      const response = await this.http.fetchWithTimeout(
        `${this.apiEndpoint}/health`,
        { method: 'GET' },
        `health-${Date.now()}`
      )
      return response.ok
    } catch {
      return false
    }
  }
}

// =============================================================================
// FACTORY
// =============================================================================

let clientInstance: ComplianceClient | null = null

export function getComplianceClient(options?: ComplianceClientOptions): ComplianceClient {
  if (!clientInstance && !options) {
    throw new Error('ComplianceClient not initialized. Provide options on first call.')
  }

  if (!clientInstance && options) {
    clientInstance = new ComplianceClient(options)
  }

  return clientInstance!
}

export function resetComplianceClient(): void {
  if (clientInstance) {
    clientInstance.cancelAllRequests()
  }
  clientInstance = null
}
