import Database from 'better-sqlite3'
import path from 'path'
import { randomBytes } from 'crypto'
import fs from 'fs'
import { Employee, User, SkillDefinition } from '@skillmate/shared'
import bcrypt from 'bcryptjs'
import { v4 as uuidv4 } from 'uuid'
import { FieldEncryption } from '../services/encryption'

// Get or generate database encryption key
const getDatabaseKey = (): string => {
  let key = process.env.DATABASE_ENCRYPTION_KEY
  
  if (!key) {
    console.warn('⚠️ No DATABASE_ENCRYPTION_KEY found in environment variables.')
    console.warn('⚠️ Generating a temporary key for development.')
    console.warn('⚠️ For production, set DATABASE_ENCRYPTION_KEY in your .env file!')
    
    // Generate and save a development key
    const keyPath = path.join(process.cwd(), '.database.key')
    if (fs.existsSync(keyPath)) {
      key = fs.readFileSync(keyPath, 'utf8')
    } else {
      key = randomBytes(32).toString('hex')
      fs.writeFileSync(keyPath, key, { mode: 0o600 }) // Restrictive permissions
      console.log('💾 Development key saved to .database.key (add to .gitignore!)')
    }
  }
  
  return key
}

// Database path configuration
const getDbPath = (): string => {
  const dbPath = process.env.DATABASE_PATH || (
    process.env.NODE_ENV === 'production' 
      ? path.join(process.cwd(), 'data', 'skillmate.encrypted.db')
      : path.join(process.cwd(), 'skillmate.dev.encrypted.db')
  )
  
  // Ensure data directory exists for production
  const dbDir = path.dirname(dbPath)
  if (!fs.existsSync(dbDir)) {
    fs.mkdirSync(dbDir, { recursive: true })
  }
  
  return dbPath
}

const dbPath = getDbPath()
const dbKey = getDatabaseKey()

// Create database connection with encryption support
export const db = new Database(dbPath)

// Enable better performance and data integrity
db.pragma('journal_mode = WAL')
db.pragma('foreign_keys = ON')
db.pragma('busy_timeout = 5000')

// Add encryption helper functions to database
export const encryptedDb = {
  ...db,
  
  // Prepare statement with automatic encryption/decryption
  prepareEncrypted(sql: string) {
    return db.prepare(sql)
  },
  
  // Insert employee with encrypted fields
  insertEmployee(employee: any) {
    const encrypted = {
      ...employee,
      email: FieldEncryption.encrypt(employee.email),
      phone: FieldEncryption.encrypt(employee.phone),
      mobile: FieldEncryption.encrypt(employee.mobile),
      clearance_level: FieldEncryption.encrypt(employee.clearance_level),
      clearance_valid_until: FieldEncryption.encrypt(employee.clearance_valid_until),
      // Add search hashes for encrypted fields
      email_hash: employee.email ? FieldEncryption.hash(employee.email) : null,
      phone_hash: employee.phone ? FieldEncryption.hash(employee.phone) : null
    }

    return db.prepare(`
      INSERT INTO employees (
        id, first_name, last_name, employee_number, photo, position, official_title,
        department, email, email_hash, phone, phone_hash, mobile, office, availability,
        clearance_level, clearance_valid_until, clearance_issued_date,
        primary_unit_id,
        created_at, updated_at, created_by
      ) VALUES (
        @id, @first_name, @last_name, @employee_number, @photo, @position, @official_title,
        @department, @email, @email_hash, @phone, @phone_hash, @mobile, @office, @availability,
        @clearance_level, @clearance_valid_until, @clearance_issued_date,
        @primary_unit_id,
        @created_at, @updated_at, @created_by
      )
    `).run(encrypted)
  },
  
  // Get employee with decrypted fields
  getEmployee(id: string) {
    const employee = db.prepare(`
      SELECT * FROM employees WHERE id = ?
    `).get(id) as any
    
    if (!employee) return null
    
    // Decrypt sensitive fields
    return {
      ...employee,
      official_title: employee.official_title,
      email: FieldEncryption.decrypt(employee.email),
      phone: FieldEncryption.decrypt(employee.phone),
      mobile: FieldEncryption.decrypt(employee.mobile),
      clearance_level: FieldEncryption.decrypt(employee.clearance_level),
      clearance_valid_until: FieldEncryption.decrypt(employee.clearance_valid_until)
    }
  },
  
  // Get all employees with decrypted fields (handle decryption failures)
  getAllEmployees() {
    const employees = db.prepare(`
      SELECT * FROM employees ORDER BY last_name, first_name
    `).all() as any[]
    
    return employees.map(emp => {
      const safeDecrypt = (field: any) => {
        if (!field) return field
        try {
          return FieldEncryption.decrypt(field) || field
        } catch (error) {
          // For compatibility with old unencrypted data or different encryption keys
          return field
        }
      }
      
      return {
        ...emp,
        official_title: emp.official_title,
        email: safeDecrypt(emp.email),
        phone: safeDecrypt(emp.phone),
        mobile: safeDecrypt(emp.mobile),
        clearance_level: safeDecrypt(emp.clearance_level),
        clearance_valid_until: safeDecrypt(emp.clearance_valid_until)
      }
    })
  },
  
  // Search by encrypted field using hash
  findByEmail(email: string) {
    const emailHash = FieldEncryption.hash(email)
    return db.prepare(`
      SELECT * FROM employees WHERE email_hash = ?
    `).get(emailHash)
  }
}

export function initializeSecureDatabase() {
  // Create updated employees table with hash fields for searching
  db.exec(`
    CREATE TABLE IF NOT EXISTS employees (
      id TEXT PRIMARY KEY,
      first_name TEXT NOT NULL,
      last_name TEXT NOT NULL,
      employee_number TEXT UNIQUE NOT NULL,
      photo TEXT,
      position TEXT NOT NULL,
      official_title TEXT,
      department TEXT NOT NULL,
      email TEXT NOT NULL,
      email_hash TEXT,
      phone TEXT NOT NULL,
      phone_hash TEXT,
      mobile TEXT,
      office TEXT,
      availability TEXT NOT NULL,
      clearance_level TEXT,
      clearance_valid_until TEXT,
      clearance_issued_date TEXT,
      primary_unit_id TEXT,
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL,
      created_by TEXT NOT NULL,
      updated_by TEXT
    )
  `)

  // Add primary_unit_id column if missing (legacy DBs)
  try {
    const cols: any[] = db.prepare(`PRAGMA table_info(employees)`).all() as any
    const hasPrimaryUnitId = cols.some(c => c.name === 'primary_unit_id')
    if (!hasPrimaryUnitId) {
      db.exec(`ALTER TABLE employees ADD COLUMN primary_unit_id TEXT`)
    }
    const hasOfficialTitle = cols.some(c => c.name === 'official_title')
    if (!hasOfficialTitle) {
      db.exec(`ALTER TABLE employees ADD COLUMN official_title TEXT`)
    }
  } catch (e) {
    // ignore
  }
  
  // Add indexes for hash fields
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_employees_email_hash ON employees(email_hash);
    CREATE INDEX IF NOT EXISTS idx_employees_phone_hash ON employees(phone_hash);
  `)

  // Users table with encrypted email
  db.exec(`
    CREATE TABLE IF NOT EXISTS users (
      id TEXT PRIMARY KEY,
      username TEXT UNIQUE NOT NULL,
      email TEXT NOT NULL,
      email_hash TEXT,
      password TEXT NOT NULL,
      role TEXT NOT NULL CHECK(role IN ('admin', 'superuser', 'user')),
      employee_id TEXT,
      last_login TEXT,
      is_active INTEGER DEFAULT 1,
      created_at TEXT NOT NULL,
      updated_at TEXT NOT NULL,
      UNIQUE(email_hash)
    )
  `)
  
  // Create index for email hash
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_users_email_hash ON users(email_hash);
  `)

  // Skills table
  db.exec(`
    CREATE TABLE IF NOT EXISTS skills (
      id TEXT PRIMARY KEY,
      name TEXT NOT NULL,
      category TEXT NOT NULL,
      description TEXT,
      expires_after INTEGER
    )
  `)



  // Employee skills junction table
  db.exec(`
    CREATE TABLE IF NOT EXISTS employee_skills (
      employee_id TEXT NOT NULL,
      skill_id TEXT NOT NULL,
      level TEXT,
      verified INTEGER DEFAULT 0,
      verified_by TEXT,
      verified_date TEXT,
      PRIMARY KEY (employee_id, skill_id),
      FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE,
      FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE
    )
  `)

  // Language skills table
  db.exec(`
    CREATE TABLE IF NOT EXISTS language_skills (
      id TEXT PRIMARY KEY,
      employee_id TEXT NOT NULL,
      language TEXT NOT NULL,
      proficiency TEXT NOT NULL,
      certified INTEGER DEFAULT 0,
      certificate_type TEXT,
      is_native INTEGER DEFAULT 0,
      can_interpret INTEGER DEFAULT 0,
      FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
    )
  `)

  // Specializations table
  db.exec(`
    CREATE TABLE IF NOT EXISTS specializations (
      id TEXT PRIMARY KEY,
      employee_id TEXT NOT NULL,
      name TEXT NOT NULL,
      FOREIGN KEY (employee_id) REFERENCES employees(id) ON DELETE CASCADE
    )
  `)

  // Audit Log for security tracking
  db.exec(`
    CREATE TABLE IF NOT EXISTS security_audit_log (
      id TEXT PRIMARY KEY,
      entity_type TEXT NOT NULL,
      entity_id TEXT NOT NULL,
      action TEXT NOT NULL CHECK(action IN ('create', 'read', 'update', 'delete', 'login', 'logout', 'failed_login')),
      user_id TEXT,
      changes TEXT,
      timestamp TEXT NOT NULL,
      ip_address TEXT,
      user_agent TEXT,
      risk_level TEXT CHECK(risk_level IN ('low', 'medium', 'high', 'critical'))
    );
    
    CREATE INDEX IF NOT EXISTS idx_security_audit_entity ON security_audit_log(entity_type, entity_id);
    CREATE INDEX IF NOT EXISTS idx_security_audit_user ON security_audit_log(user_id);
    CREATE INDEX IF NOT EXISTS idx_security_audit_timestamp ON security_audit_log(timestamp);
    CREATE INDEX IF NOT EXISTS idx_security_audit_risk ON security_audit_log(risk_level);
  `)

  // System settings table
  db.exec(`
    CREATE TABLE IF NOT EXISTS system_settings (
      key TEXT PRIMARY KEY,
      value TEXT NOT NULL,
      description TEXT,
      updated_at TEXT NOT NULL,
      updated_by TEXT
    )
  `)

  // Insert default system settings
  const settingsExist = db.prepare('SELECT key FROM system_settings WHERE key = ?').get('email_notifications_enabled')
  if (!settingsExist) {
    const now = new Date().toISOString()
    db.prepare(`
      INSERT INTO system_settings (key, value, description, updated_at, updated_by)
      VALUES (?, ?, ?, ?, ?)
    `).run('email_notifications_enabled', 'false', 'Enable/disable email notifications for new user passwords', now, 'system')
  }

  // Create indexes for better performance
  db.exec(`
    CREATE INDEX IF NOT EXISTS idx_employees_availability ON employees(availability);
    CREATE INDEX IF NOT EXISTS idx_employees_department ON employees(department);
    CREATE INDEX IF NOT EXISTS idx_employees_primary_unit ON employees(primary_unit_id);
    CREATE INDEX IF NOT EXISTS idx_employee_skills_employee ON employee_skills(employee_id);
    CREATE INDEX IF NOT EXISTS idx_employee_skills_skill ON employee_skills(skill_id);
    CREATE INDEX IF NOT EXISTS idx_language_skills_employee ON language_skills(employee_id);
    CREATE INDEX IF NOT EXISTS idx_specializations_employee ON specializations(employee_id);
  `)

  // Controlled vocabulary (used to store skill categories/subcategories names)
  db.exec(`
    CREATE TABLE IF NOT EXISTS controlled_vocabulary (
      id TEXT PRIMARY KEY,
      category TEXT NOT NULL,
      value TEXT NOT NULL,
      description TEXT,
      is_active INTEGER DEFAULT 1,
      created_at TEXT NOT NULL,
      UNIQUE(category, value)
    );
    CREATE INDEX IF NOT EXISTS idx_vocab_category ON controlled_vocabulary(category);
    CREATE INDEX IF NOT EXISTS idx_vocab_value ON controlled_vocabulary(value);
  `)

  // Create default admin user if not exists
  const adminExists = db.prepare('SELECT id FROM users WHERE username = ?').get('admin')
  if (!adminExists) {
    const hashedPassword = bcrypt.hashSync('admin123', 12)
    const now = new Date().toISOString()
    const adminEmail = 'admin@skillmate.local'
    
    db.prepare(`
      INSERT INTO users (id, username, email, email_hash, password, role, is_active, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
    `).run(
      uuidv4(),
      'admin',
      FieldEncryption.encrypt(adminEmail),
      FieldEncryption.hash(adminEmail),
      hashedPassword,
      'admin',
      1,
      now,
      now
    )
    
    console.log('🔐 Default admin user created with password: admin123')
    console.log('⚠️  Please change this password immediately!')
  }
}

// db is already exported above via export const db = new Database(dbPath)
