import bcrypt from 'bcryptjs'
import jwt from 'jsonwebtoken'
import { promises as fs } from 'fs'
import path from 'path'
import { encryptObject, decryptObject } from './encryption.js'

// Export migrateUserRoles für Verwendung in anderen Modulen
export function migrateUserRoles(user) {
  if (!user) return user
  
  // Wenn bereits roles Array vorhanden, nichts tun
  if (Array.isArray(user.roles)) {
    return user
  }
  
  // Wenn role vorhanden, zu roles Array konvertieren
  if (user.role) {
    user.roles = [user.role]
    delete user.role
  } else {
    // Fallback: Standard-Rolle
    user.roles = ['mitglied']
  }
  
  return user
}

const JWT_SECRET = process.env.JWT_SECRET || 'harheimertc-secret-key-change-in-production'

// 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., 'users.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 USERS_FILE = getDataPath('users.json')
const SESSIONS_FILE = getDataPath('sessions.json')

// Get encryption key from environment
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 trying to parse as JSON first
function isEncrypted(data) {
  try {
    const parsed = JSON.parse(data.trim())
    if (Array.isArray(parsed)) {
      return false // Unencrypted array
    }
    if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
      return false
    }
    return false
  } catch {
    // JSON parsing failed - likely encrypted base64
    return true
  }
}

// Read users from file (with encryption support and migration)
export async function readUsers() {
  try {
    const data = await fs.readFile(USERS_FILE, 'utf-8')
    
    const encrypted = isEncrypted(data)
    let users = []
    
    if (encrypted) {
      // Versuche mit verschiedenen Schlüsseln zu entschlüsseln (für Migration)
      const possibleKeys = getPossibleEncryptionKeys()
      const encryptionKey = getEncryptionKey()
      
      let lastError = null
      for (let i = 0; i < possibleKeys.length; i++) {
        const key = possibleKeys[i]
        try {
          users = decryptObject(data, key)
          
          // Wenn mit altem Schlüssel entschlüsselt wurde, warnen und neu verschlüsseln
          if (i > 0 && key !== encryptionKey) {
            console.warn(`⚠️  Benutzerdaten wurden mit altem Schlüssel entschlüsselt. Automatische Neuverschlüsselung...`)
            try {
              await writeUsers(users)
              console.log('✅ Benutzerdaten 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')
            }
          }
          
          break // Erfolgreich entschlüsselt
        } catch (decryptError) {
          lastError = decryptError
          // Versuche nächsten Schlüssel
          continue
        }
      }
      
      // Wenn alle Schlüssel fehlgeschlagen sind
      if (!users || users.length === 0) {
        console.error('Fehler beim Entschlüsseln der Benutzerdaten:')
        console.error('  Versuchte Schlüssel:', possibleKeys.length)
        console.error('  Letzter Fehler:', lastError?.message || 'Unbekannter Fehler')
        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
        try {
          users = JSON.parse(data)
          console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen')
        } catch (_parseError) {
          console.error('Konnte Benutzerdaten weder entschlüsseln noch als JSON lesen')
          return []
        }
      }
    } else {
      // Plain JSON - migrate to encrypted format
      users = JSON.parse(data)
      console.log('Migriere unverschlüsselte Benutzerdaten zu verschlüsselter Speicherung...')
    }
    
    // Migriere Rollen von role zu roles Array
    let needsMigration = false
    users = users.map(user => {
      const migrated = migrateUserRoles(user)
      if (!Array.isArray(user.roles) && user.role) {
        needsMigration = true
      }
      return migrated
    })
    
    // Wenn Migration nötig war, speichere zurück
    if (needsMigration) {
      console.log('Migriere Rollen von role zu roles Array...')
      await writeUsers(users)
    } else if (!encrypted) {
      // Write back encrypted wenn noch nicht verschlüsselt
      await writeUsers(users)
    }
    
    return users
  } catch (error) {
    if (error.code === 'ENOENT') {
      return []
    }
    console.error('Fehler beim Lesen der Benutzerdaten:', error)
    return []
  }
}

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

// Prüft ob Sessions-Daten verschlüsselt sind
function isSessionsEncrypted(data) {
  try {
    const parsed = JSON.parse(data.trim())
    if (Array.isArray(parsed)) {
      return false
    }
    if (typeof parsed === 'object' && parsed !== null && !parsed.encryptedData) {
      return false
    }
    return false
  } catch (e) {
    return true
  }
}

// Read sessions from file (with encryption support)
export async function readSessions() {
  try {
    const data = await fs.readFile(SESSIONS_FILE, 'utf-8')
    const encrypted = isSessionsEncrypted(data)
    
    if (encrypted) {
      const encryptionKey = getEncryptionKey()
      try {
        return decryptObject(data, encryptionKey)
      } catch (decryptError) {
        console.error('Fehler beim Entschlüsseln der Sessions:', decryptError)
        try {
          const plainData = JSON.parse(data)
          console.warn('Entschlüsselung fehlgeschlagen, versuche als unverschlüsseltes Format zu lesen')
          return plainData
        } catch (_parseError) {
          console.error('Konnte Sessions weder entschlüsseln noch als JSON lesen')
          return []
        }
      }
    } else {
      // Plain JSON - migriere zu verschlüsselter Speicherung
      const sessions = JSON.parse(data)
      console.log('Migriere unverschlüsselte Sessions zu verschlüsselter Speicherung...')
      await writeSessions(sessions)
      return sessions
    }
  } catch (error) {
    if (error.code === 'ENOENT') {
      return []
    }
    console.error('Fehler beim Lesen der Sessions:', error)
    return []
  }
}

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

// Hash password
export async function hashPassword(password) {
  const salt = await bcrypt.genSalt(10)
  return await bcrypt.hash(password, salt)
}

// Verify password
export async function verifyPassword(password, hash) {
  return await bcrypt.compare(password, hash)
}

// Generate JWT token
export function generateToken(user) {
  // Stelle sicher, dass Rollen migriert sind
  const migratedUser = migrateUserRoles({ ...user })
  const roles = Array.isArray(migratedUser.roles) ? migratedUser.roles : (migratedUser.role ? [migratedUser.role] : ['mitglied'])
  
  return jwt.sign(
    {
      id: user.id,
      email: user.email,
      roles: roles
    },
    JWT_SECRET,
    { expiresIn: '7d' }
  )
}

// Verify JWT token
export function verifyToken(token) {
  try {
    return jwt.verify(token, JWT_SECRET)
  } catch (_error) {
    return null
  }
}

// Get user by ID
export async function getUserById(id) {
  const users = await readUsers()
  const user = users.find(u => u.id === id)
  return user ? migrateUserRoles(user) : null
}

// Get user by email
export async function getUserByEmail(email) {
  const users = await readUsers()
  const user = users.find(u => u.email === email)
  return user ? migrateUserRoles(user) : null
}


// Prüft ob Benutzer eine bestimmte Rolle hat
export function hasRole(user, role) {
  if (!user) return false
  const roles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
  return roles.includes(role)
}

// Prüft ob Benutzer eine der angegebenen Rollen hat
export function hasAnyRole(user, ...roles) {
  if (!user) return false
  const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
  return roles.some(role => userRoles.includes(role))
}

// Prüft ob Benutzer alle angegebenen Rollen hat
export function hasAllRoles(user, ...roles) {
  if (!user) return false
  const userRoles = Array.isArray(user.roles) ? user.roles : (user.role ? [user.role] : [])
  return roles.every(role => userRoles.includes(role))
}

// Get user from token
export async function getUserFromToken(token) {
  const decoded = verifyToken(token)
  if (!decoded) return null

  const users = await readUsers()
  const user = users.find(u => u.id === decoded.id)
  
  // Migriere Rollen beim Laden
  if (user) {
    migrateUserRoles(user)
  }
  
  return user
}

// Create session
export async function createSession(userId, token) {
  const sessions = await readSessions()
  const session = {
    id: Date.now().toString(),
    userId,
    token,
    createdAt: new Date().toISOString(),
    expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString() // 7 days
  }
  sessions.push(session)
  await writeSessions(sessions)
  return session
}

// Delete session
export async function deleteSession(token) {
  const sessions = await readSessions()
  const filtered = sessions.filter(s => s.token !== token)
  await writeSessions(filtered)
}

// Clean expired sessions
export async function cleanExpiredSessions() {
  const sessions = await readSessions()
  const now = new Date()
  const valid = sessions.filter(s => new Date(s.expiresAt) > now)
  await writeSessions(valid)
}

