import { promises as fs } from 'fs'
import path from 'path'
import { randomUUID } from 'crypto'
import { encrypt, decrypt, encryptObject, decryptObject } from './encryption.js'

// Handle both dev and production paths
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
// filename is always a hardcoded constant (e.g., 'members.json'), never user input
const getDataPath = (filename) => {
  const cwd = process.cwd()
  
  // In production (.output/server), working dir is .output
  if (cwd.endsWith('.output')) {
    // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
    return path.join(cwd, '../server/data', filename)
  }
  
  // In development, working dir is project root
  // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
  return path.join(cwd, 'server/data', filename)
}

const MEMBERS_FILE = getDataPath('members.json')

// Get encryption key from environment or config
function getEncryptionKey() {
  return process.env.ENCRYPTION_KEY || 'local_development_encryption_key_change_in_production'
}

// Liste möglicher alter Verschlüsselungsschlüssel (für Migration)
function getPossibleEncryptionKeys() {
  const currentKey = getEncryptionKey()
  const oldKeys = [
    'default-key-change-in-production',
    'local_development_encryption_key_change_in_production'
  ]
  
  // Aktueller Schlüssel zuerst, dann alte Schlüssel
  const keys = [currentKey]
  for (const oldKey of oldKeys) {
    if (oldKey !== currentKey) {
      keys.push(oldKey)
    }
  }
  
  // Optional: Alter Schlüssel aus Environment-Variable
  if (process.env.OLD_ENCRYPTION_KEY && process.env.OLD_ENCRYPTION_KEY !== currentKey) {
    keys.push(process.env.OLD_ENCRYPTION_KEY)
  }
  
  return keys
}

// Check if data is encrypted by checking format indicators
function isEncrypted(data) {
  if (!data || typeof data !== 'string') {
    return false
  }

  const trimmed = data.trim()
  
  // Empty or whitespace only
  if (!trimmed) {
    return false
  }

  // Check for v2 prefix (v2:base64...)
  if (trimmed.startsWith('v2:')) {
    return true
  }

  // Try to parse as JSON - if successful, it's likely unencrypted
  try {
    const parsed = JSON.parse(trimmed)
    // If it's an array (members list) or object with member-like structure, it's unencrypted
    if (Array.isArray(parsed)) {
      return false // Unencrypted array
    }
    // If it's an object but not encrypted format, it's unencrypted
    if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
      return false
    }
    return false
  } catch (_e) {
    // JSON parsing failed - check if it looks like base64 (encrypted)
    // Base64 strings typically don't contain spaces and have specific character set
    const base64Pattern = /^[A-Za-z0-9+/=]+$/
    if (base64Pattern.test(trimmed) && trimmed.length > 50) {
      // Looks like base64 and long enough to be encrypted data
      return true
    }
    // If it's not valid JSON and doesn't look like base64, assume unencrypted but malformed
    return false
  }
}

// Read manual members from file (with encryption support and migration)
export async function readMembers() {
  try {
    const data = await fs.readFile(MEMBERS_FILE, 'utf-8')
    
    // Check if file is empty or whitespace only
    if (!data || !data.trim()) {
      console.warn('Mitgliederdaten-Datei ist leer')
      return []
    }
    
    // Check if data is encrypted or plain JSON
    const encrypted = isEncrypted(data)
    
    if (encrypted) {
      // Versuche mit verschiedenen Schlüsseln zu entschlüsseln (für Migration)
      const possibleKeys = getPossibleEncryptionKeys()
      const encryptionKey = getEncryptionKey()
      
      // Warn if using default encryption key in production
      if (process.env.NODE_ENV === 'production' && 
          encryptionKey === 'local_development_encryption_key_change_in_production') {
        console.error('WARNUNG: Produktionsumgebung verwendet Standard-Verschlüsselungsschlüssel!')
      }
      
      let lastError = null
      for (let i = 0; i < possibleKeys.length; i++) {
        const key = possibleKeys[i]
        try {
          const decrypted = decryptObject(data, key)
          
          // Wenn mit altem Schlüssel entschlüsselt wurde, warnen und neu verschlüsseln
          if (i > 0 && key !== encryptionKey) {
            console.warn(`⚠️  Mitgliederdaten wurden mit altem Schlüssel entschlüsselt. Automatische Neuverschlüsselung...`)
            try {
              await writeMembers(decrypted)
              console.log('✅ Mitgliederdaten erfolgreich mit neuem Schlüssel neu verschlüsselt')
            } catch (writeError) {
              console.error('❌ Fehler beim Neuverschlüsseln:', writeError.message)
              console.error('   Bitte führen Sie manuell aus: node scripts/re-encrypt-data.js')
            }
          }
          
          return decrypted
        } catch (decryptError) {
          lastError = decryptError
          // Versuche nächsten Schlüssel
          continue
        }
      }
      
      // Alle Schlüssel fehlgeschlagen
      console.error('Fehler beim Entschlüsseln der Mitgliederdaten:')
      console.error('  Versuchte Schlüssel:', possibleKeys.length)
      console.error('  Letzter Fehler:', lastError?.message || 'Unbekannter Fehler')
      console.error('  Daten-Länge:', data.length)
      console.error('  Daten-Präfix (erste 50 Zeichen):', data.substring(0, 50))
      console.error('')
      console.error('💡 Lösung: Führen Sie das Re-Encrypt-Skript aus:')
      console.error('   node scripts/re-encrypt-data.js --old-key="<alter-schlüssel>"')
      console.error('   Oder setzen Sie OLD_ENCRYPTION_KEY als Environment-Variable')
      
      // Fallback: try to read as plain JSON (migration scenario)
      try {
        const plainData = JSON.parse(data.trim())
        console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen')
        // Don't auto-migrate if decryption failed - might be wrong key
        return plainData
      } catch (parseError) {
        console.error('Konnte Mitgliederdaten weder entschlüsseln noch als JSON lesen')
        console.error('JSON-Parse-Fehler:', parseError.message)
        // Return empty array to prevent complete failure
        return []
      }
    } else {
      // Plain JSON - migrate to encrypted format
      const members = JSON.parse(data.trim())
      console.log('Migriere unverschlüsselte Mitgliederdaten zu verschlüsselter Speicherung...')
      
      // Write back encrypted
      await writeMembers(members)
      
      return members
    }
  } catch (error) {
    if (error.code === 'ENOENT') {
      return []
    }
    console.error('Fehler beim Lesen der Mitgliederdaten:', error)
    return []
  }
}

// Write manual members to file (always encrypted)
export async function writeMembers(members) {
  try {
    const encryptionKey = getEncryptionKey()
    const encryptedData = encryptObject(members, encryptionKey)
    await fs.writeFile(MEMBERS_FILE, encryptedData, 'utf-8')
    return true
  } catch (error) {
    console.error('Fehler beim Schreiben der Mitgliederdaten:', error)
    return false
  }
}

// Get member by ID
export async function getMemberById(id) {
  const members = await readMembers()
  return members.find(m => m.id === id)
}

// Normalize date string for comparison (handles different date formats)
export function normalizeDate(dateString) {
  if (!dateString) return ''
  // Try to parse and normalize to ISO format (YYYY-MM-DD)
  try {
    const date = new Date(dateString)
    if (isNaN(date.getTime())) return dateString.trim()
    return date.toISOString().split('T')[0]
  } catch (_e) {
    return dateString.trim()
  }
}

// Check for duplicate member based on firstName, lastName, and geburtsdatum
function findDuplicateMember(members, firstName, lastName, geburtsdatum) {
  const normalizedFirstName = (firstName || '').trim().toLowerCase()
  const normalizedLastName = (lastName || '').trim().toLowerCase()
  const normalizedDate = normalizeDate(geburtsdatum)
  
  return members.find(m => {
    const mFirstName = (m.firstName || '').trim().toLowerCase()
    const mLastName = (m.lastName || '').trim().toLowerCase()
    const mDate = normalizeDate(m.geburtsdatum)
    
    return mFirstName === normalizedFirstName &&
           mLastName === normalizedLastName &&
           mDate === normalizedDate &&
           mDate !== '' // Only match if date is provided for both
  })
}

// Add or update manual member
export async function saveMember(memberData) {
  const members = await readMembers()
  
  if (memberData.id) {
    // Update existing manual member if present.
    // If the ID belongs to a login-only member shown in the merged list,
    // update an existing manual duplicate or create a manual overlay.
    const index = members.findIndex(m => m.id === memberData.id)
    const duplicate = findDuplicateMember(
      members.filter(m => m.id !== memberData.id),
      memberData.firstName,
      memberData.lastName,
      memberData.geburtsdatum
    )

    if (duplicate && index !== -1) {
      throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
    }

    if (index !== -1) {
      members[index] = { ...members[index], ...memberData }
    } else if (duplicate) {
      const duplicateIndex = members.findIndex(m => m.id === duplicate.id)
      members[duplicateIndex] = {
        ...members[duplicateIndex],
        ...memberData,
        id: members[duplicateIndex].id
      }
    } else {
      members.push({
        ...memberData,
        active: typeof memberData.active === 'boolean' ? memberData.active : true
      })
    }
  } else {
    // Add new - check for duplicate first
    if (memberData.firstName && memberData.lastName && memberData.geburtsdatum) {
      const duplicate = findDuplicateMember(
        members,
        memberData.firstName,
        memberData.lastName,
        memberData.geburtsdatum
      )
      
      if (duplicate) {
        throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
      }
    }
    
    // Add new - use UUID for guaranteed uniqueness
    const newMember = {
      ...memberData,
      id: randomUUID() // Cryptographically secure unique ID
    }
    members.push(newMember)
  }
  
  await writeMembers(members)
  return true
}

// Delete manual member
export async function deleteMember(id) {
  const members = await readMembers()
  const filtered = members.filter(m => m.id !== id)
  await writeMembers(filtered)
  return true
}

