import { type ClassValue, clsx } from 'clsx'
import { twMerge } from 'tailwind-merge'
import type { Table } from './table'
import type { Artifact } from '@/types/messages'

interface AppUser {
  login: string
  email: string
  displayName: string
}

interface AppConfig {
  xsrfCookieName: string
  user?: AppUser
  llmSettings?: {
    traceExplorerDefaultWebApp?: {
      projectKey?: string
      webAppId?: string
    }
  }
  dssExternalURL?: string
  version?: {
    conf_version?: string
    product_version?: string
    product_commitid?: string
  }
}

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs))
}

export const isLocal = () => {
  let dataikuLib: any
  try {
    /* global */ /* eslint-disable */ // @ts-ignore
    dataikuLib = dataiku /* eslint-enable */
  } catch {
    dataikuLib = undefined
  }

  return !dataikuLib
}

export function getAppConfig(): AppConfig | null {
  try {
    return (window as any).dkuUsageReportingUtils.getAppConfig() as AppConfig
  } catch (error) {
    console.error('Failed to fetch app config:', error)
    return null
  }
}

// Add utility function to get cookie value
export function getCookie(name: string): string | null {
  const value = `; ${document.cookie}`
  const parts = value.split(`; ${name}=`)
  if (parts.length === 2) return parts.pop()?.split(';').shift() || null
  return null
}

// Add utility function to get XSRF token
export function getXsrfToken(): string | null {
  const appConfig = getAppConfig()
  if (!appConfig?.xsrfCookieName) return null
  return getCookie(appConfig.xsrfCookieName)
}

// Get DSS version from app config
export function getDssVersion(): string {
  const appConfig = getAppConfig()
  return appConfig?.version?.product_version || '0.0.0'
}

// Get current user from app config
export function getUser(): AppUser | null {
  const appConfig = getAppConfig()
  return appConfig?.user || null
}

// Export the AppConfig interface for use in other components
export type { AppConfig, AppUser }

export function toCsvString(tbl: Table): string {
  const escape = (val: unknown): string => {
    if (val === null || val === undefined) return ''
    const s = String(val)
    // Escape CSV per RFC 4180
    if (/[",\n\r]/.test(s)) return `"${s.replace(/"/g, '""')}"`
    return s
  }

  const header = tbl.columns.map(escape).join(',')
  const rows = (tbl.data ?? []).map((row) => {
    // Row can be shorter/longer than columns; normalize to columns length
    const arr = Array.isArray(row) ? row : []
    const normalized = tbl.columns.map((_, i) => escape(arr[i]))
    return normalized.join(',')
  })

  return [header, ...rows].join('\r\n')
}
export function extractRecordsFromArtifact(artifact: Artifact): Table[] {
  const records: Table[] = []
  for (const item of artifact.parts) {
    if (item['type'] === 'RECORDS') {
      records.push({ columns: item['records']['columns'], data: item['records']['data'] })
    }
  }
  return records
}
export function extractRecordsFromArtifacts(artifacts: Array<Artifact>) {
  const records: Record<string, Table[]> = {}
  for (const [i, a] of artifacts.entries()) {
    records[a.name + i] = extractRecordsFromArtifact(a)
  }
  return records
}
export function extractRecordsFromArtifactsFlat(artifacts: Array<Artifact>) {
  const records: Table[][] = []
  for (const a of artifacts) {
    records.push(extractRecordsFromArtifact(a))
  }
  return records
}
export function getRecordsRows(records: Table) {
  const rows: any[] = []
  if (!records.columns || !records.data) return rows
  for (const d of records.data) {
    const row: Record<string, any> = {}
    for (const [i, col] of records.columns.entries()) {
      row[col] = d[i]
    }
    rows.push(row)
  }
  return rows
}

export function getRecordsColumns(records: Table) {
  return records.columns?.map((col: string) => {
    return { name: col, label: col, field: col, align: 'left' }
  })
}

/**
 * Highlights matching text by wrapping it in a mark element
 * Returns an array of objects with text and highlighted flag for v-for rendering
 */
export function highlightText(
  text: string,
  query: string
): Array<{ text: string; highlighted: boolean }> {
  if (!query || !text) {
    return [{ text: text || '', highlighted: false }]
  }

  const lowerText = text.toLowerCase()
  const lowerQuery = query.toLowerCase()
  const result: Array<{ text: string; highlighted: boolean }> = []

  let lastIndex = 0
  let index = lowerText.indexOf(lowerQuery)

  while (index !== -1) {
    // Add non-highlighted text before match
    if (index > lastIndex) {
      result.push({ text: text.slice(lastIndex, index), highlighted: false })
    }
    // Add highlighted match (preserve original case)
    result.push({ text: text.slice(index, index + query.length), highlighted: true })
    lastIndex = index + query.length
    index = lowerText.indexOf(lowerQuery, lastIndex)
  }

  // Add remaining text
  if (lastIndex < text.length) {
    result.push({ text: text.slice(lastIndex), highlighted: false })
  }

  return result.length > 0 ? result : [{ text, highlighted: false }]
}

export function compareVersions(version1: string, version2: string): number {
  try {
    // Normalize versions
    const v1 = (version1 || '').trim()
    const v2 = (version2 || '').trim()

    // Dev versions are always considered greater than any other version
    const isDev1 = v1.startsWith('dev/')
    const isDev2 = v2.startsWith('dev/')

    if (isDev1 && isDev2) return 0 // Both dev versions are equal
    if (isDev1) return 1 // v1 is dev, so it's greater
    if (isDev2) return -1 // v2 is dev, so it's greater

    // Strip pre-release identifiers (alpha, beta, rc, etc.)
    // e.g., "14.3.0-alpha2" becomes "14.3.0"
    const stripPreRelease = (v: string) => v.split('-')[0].split('+')[0]
    const clean1 = stripPreRelease(v1)
    const clean2 = stripPreRelease(v2)

    // Split and convert to numbers
    const parts1 = clean1.split('.').map((n) => parseInt(n, 10))
    const parts2 = clean2.split('.').map((n) => parseInt(n, 10))

    // Compare each part
    const maxLength = Math.max(parts1.length, parts2.length)
    for (let i = 0; i < maxLength; i++) {
      const num1 = parts1[i] || 0
      const num2 = parts2[i] || 0

      // Handle NaN cases (treat as 0)
      const safe1 = isNaN(num1) ? 0 : num1
      const safe2 = isNaN(num2) ? 0 : num2

      if (safe1 > safe2) return 1
      if (safe1 < safe2) return -1
    }

    return 0
  } catch (error) {
    // If any error occurs during comparison, log it and return 0 (equal)
    console.error('Error comparing versions:', error)
    return 0
  }
}
