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'
}

// Check if data is encrypted by trying to parse as JSON first
function isEncrypted(data) {
  try {
    // Try to parse as JSON - if successful and looks like member data, it's unencrypted
    const parsed = JSON.parse(data.trim())
    // 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 - likely encrypted base64
    return true
  }
}

// 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 data is encrypted or plain JSON
    const encrypted = isEncrypted(data)
    
    if (encrypted) {
      // Decrypt and parse
      const encryptionKey = getEncryptionKey()
      try {
        return decryptObject(data, encryptionKey)
      } catch (decryptError) {
        console.error('Fehler beim Entschlüsseln der Mitgliederdaten:', decryptError)
        // Fallback: try to read as plain JSON (migration scenario)
        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 Mitgliederdaten weder entschlüsseln noch als JSON lesen')
          return []
        }
      }
    } else {
      // Plain JSON - migrate to encrypted format
      const members = JSON.parse(data)
      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
    const index = members.findIndex(m => m.id === memberData.id)
    if (index !== -1) {
      // Check for duplicate (excluding current member)
      const duplicate = findDuplicateMember(
        members.filter(m => m.id !== memberData.id),
        memberData.firstName,
        memberData.lastName,
        memberData.geburtsdatum
      )
      
      if (duplicate) {
        throw new Error('Ein Mitglied mit diesem Namen und Geburtsdatum existiert bereits.')
      }
      
      members[index] = { ...members[index], ...memberData }
    } else {
      throw new Error('Mitglied nicht gefunden')
    }
  } 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
}

