const sqlite3 = require('sqlite3').verbose();
const path = require('path');
const fs = require('fs');
const logger = require('../utils/logger');

const DB_PATH = process.env.DB_PATH || path.join(__dirname, '../../data/streamflow.db');

// Ensure data directory exists
const dataDir = path.dirname(DB_PATH);
if (!fs.existsSync(dataDir)) {
  fs.mkdirSync(dataDir, { recursive: true, mode: 0o755 });
}

const db = new sqlite3.Database(DB_PATH, (err) => {
  if (err) {
    logger.error('Database connection error:', err);
  } else {
    logger.info('Connected to SQLite database');
  }
});

const initialize = () => {
  // Initialize RBAC roles first (must be before user creation)
  const { initializeRoles } = require('../middleware/rbac');
  initializeRoles();
  db.serialize(() => {
    // Users table
    db.run(`CREATE TABLE IF NOT EXISTS users (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT UNIQUE NOT NULL,
      email TEXT UNIQUE NOT NULL,
      password TEXT NOT NULL,
      role TEXT DEFAULT 'user',
      must_change_password BOOLEAN DEFAULT 0,
      is_active BOOLEAN DEFAULT 1,
      two_factor_enabled BOOLEAN DEFAULT 0,
      two_factor_secret TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      created_by INTEGER,
      FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
    )`);

    // 2FA backup codes table
    db.run(`CREATE TABLE IF NOT EXISTS two_factor_backup_codes (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      code TEXT NOT NULL,
      used BOOLEAN DEFAULT 0,
      used_at DATETIME,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Password history table (prevent password reuse)
    db.run(`CREATE TABLE IF NOT EXISTS password_history (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      password_hash TEXT NOT NULL,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Login attempts tracking table
    db.run(`CREATE TABLE IF NOT EXISTS login_attempts (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      username TEXT NOT NULL,
      ip_address TEXT NOT NULL,
      user_agent TEXT,
      success BOOLEAN NOT NULL,
      failure_reason TEXT,
      timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
    )`);

    // Create indexes for login_attempts
    db.run('CREATE INDEX IF NOT EXISTS idx_username_timestamp ON login_attempts(username, timestamp)');
    db.run('CREATE INDEX IF NOT EXISTS idx_ip_timestamp ON login_attempts(ip_address, timestamp)');

    // Account lockouts table
    db.run(`CREATE TABLE IF NOT EXISTS account_lockouts (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      locked_until DATETIME NOT NULL,
      locked_by TEXT DEFAULT 'system',
      reason TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Active sessions table
    db.run(`CREATE TABLE IF NOT EXISTS active_sessions (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      session_token TEXT UNIQUE NOT NULL,
      ip_address TEXT,
      user_agent TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      last_activity DATETIME DEFAULT CURRENT_TIMESTAMP,
      expires_at DATETIME NOT NULL,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Security audit log table
    db.run(`CREATE TABLE IF NOT EXISTS security_audit_log (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      event_type TEXT NOT NULL,
      user_id INTEGER,
      ip_address TEXT,
      user_agent TEXT,
      success BOOLEAN NOT NULL,
      failure_reason TEXT,
      metadata TEXT,
      timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
    )`);

    // Create indexes for security_audit_log
    db.run('CREATE INDEX IF NOT EXISTS idx_event_timestamp ON security_audit_log(event_type, timestamp)');
    db.run('CREATE INDEX IF NOT EXISTS idx_user_timestamp ON security_audit_log(user_id, timestamp)');

    // Add new columns if they don't exist (migration)
    db.all("PRAGMA table_info(users)", [], (err, columns) => {
      if (!err) {
        if (!columns.some(col => col.name === 'must_change_password')) {
          db.run("ALTER TABLE users ADD COLUMN must_change_password BOOLEAN DEFAULT 0");
        }
        if (!columns.some(col => col.name === 'is_active')) {
          db.run("ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT 1");
        }
        if (!columns.some(col => col.name === 'created_by')) {
          db.run("ALTER TABLE users ADD COLUMN created_by INTEGER REFERENCES users(id) ON DELETE SET NULL");
        }
        if (!columns.some(col => col.name === 'two_factor_enabled')) {
          db.run("ALTER TABLE users ADD COLUMN two_factor_enabled BOOLEAN DEFAULT 0");
        }
        if (!columns.some(col => col.name === 'two_factor_secret')) {
          db.run("ALTER TABLE users ADD COLUMN two_factor_secret TEXT");
        }
        if (!columns.some(col => col.name === 'password_changed_at')) {
          db.run("ALTER TABLE users ADD COLUMN password_changed_at DATETIME DEFAULT CURRENT_TIMESTAMP");
        }
        if (!columns.some(col => col.name === 'password_expires_at')) {
          db.run("ALTER TABLE users ADD COLUMN password_expires_at DATETIME");
        }
        if (!columns.some(col => col.name === 'failed_login_attempts')) {
          db.run("ALTER TABLE users ADD COLUMN failed_login_attempts INTEGER DEFAULT 0");
        }
        if (!columns.some(col => col.name === 'last_failed_login')) {
          db.run("ALTER TABLE users ADD COLUMN last_failed_login DATETIME");
        }
        if (!columns.some(col => col.name === 'locked_until')) {
          db.run("ALTER TABLE users ADD COLUMN locked_until DATETIME");
        }
        if (!columns.some(col => col.name === 'last_login_at')) {
          db.run("ALTER TABLE users ADD COLUMN last_login_at DATETIME");
        }
        if (!columns.some(col => col.name === 'last_login_ip')) {
          db.run("ALTER TABLE users ADD COLUMN last_login_ip TEXT");
        }
      }
    });

    // Create default admin user if no users exist
    db.get("SELECT COUNT(*) as count FROM users", [], (err, result) => {
      if (!err && result.count === 0) {
        const bcrypt = require('bcrypt');
        const defaultPassword = bcrypt.hashSync('admin', 10);
        
        db.run(
          `INSERT INTO users (username, email, password, role, must_change_password) 
           VALUES (?, ?, ?, ?, ?)`,
          ['admin', 'admin@streamflow.local', defaultPassword, 'admin', 1],
          (err) => {
            if (err) {
              console.error('Failed to create default admin:', err);
            } else {
              // CWE-532: Never log passwords - even default ones
              console.log('✓ Default admin user created (username: admin)');
              console.log('⚠ SECURITY: Change the default admin password immediately!');
            }
          }
        );
      }
    });

    // Profiles table (multi-user support)
    db.run(`CREATE TABLE IF NOT EXISTS profiles (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      avatar TEXT,
      is_child BOOLEAN DEFAULT 0,
      age_restriction INTEGER DEFAULT 18,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Playlists table
    db.run(`CREATE TABLE IF NOT EXISTS playlists (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      url TEXT,
      type TEXT DEFAULT 'live',
      filename TEXT,
      category TEXT,
      is_active BOOLEAN DEFAULT 1,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Channels table
    db.run(`CREATE TABLE IF NOT EXISTS channels (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      playlist_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      url TEXT NOT NULL,
      logo TEXT,
      custom_logo TEXT,
      group_name TEXT,
      tvg_id TEXT,
      tvg_name TEXT,
      language TEXT,
      country TEXT,
      is_radio BOOLEAN DEFAULT 0,
      is_active BOOLEAN DEFAULT 1,
      health_status TEXT DEFAULT 'unknown',
      last_checked DATETIME,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE
    )`);

    // Add custom_logo column if it doesn't exist (migration)
    db.all("PRAGMA table_info(channels)", [], (err, columns) => {
      if (!err && !columns.some(col => col.name === 'custom_logo')) {
        db.run("ALTER TABLE channels ADD COLUMN custom_logo TEXT", (err) => {
          if (err) console.error('Migration error:', err);
        });
      }
    });

    // Custom groups table
    db.run(`CREATE TABLE IF NOT EXISTS custom_groups (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      icon TEXT,
      order_index INTEGER DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Group channels (many-to-many)
    db.run(`CREATE TABLE IF NOT EXISTS group_channels (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      group_id INTEGER NOT NULL,
      channel_id INTEGER NOT NULL,
      order_index INTEGER DEFAULT 0,
      FOREIGN KEY (group_id) REFERENCES custom_groups(id) ON DELETE CASCADE,
      FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,
      UNIQUE(group_id, channel_id)
    )`);

    // Recordings table
    db.run(`CREATE TABLE IF NOT EXISTS recordings (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      profile_id INTEGER,
      channel_id INTEGER NOT NULL,
      title TEXT NOT NULL,
      description TEXT,
      start_time DATETIME NOT NULL,
      end_time DATETIME NOT NULL,
      duration INTEGER,
      file_path TEXT,
      file_size INTEGER,
      status TEXT DEFAULT 'scheduled',
      is_series BOOLEAN DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE SET NULL,
      FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
    )`);

    // Watch history table
    db.run(`CREATE TABLE IF NOT EXISTS watch_history (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      profile_id INTEGER,
      channel_id INTEGER NOT NULL,
      watched_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      duration INTEGER,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE SET NULL,
      FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE
    )`);

    // Favorites table
    db.run(`CREATE TABLE IF NOT EXISTS favorites (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      profile_id INTEGER,
      channel_id INTEGER NOT NULL,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE SET NULL,
      FOREIGN KEY (channel_id) REFERENCES channels(id) ON DELETE CASCADE,
      UNIQUE(user_id, profile_id, channel_id)
    )`);

    // Settings table
    db.run(`CREATE TABLE IF NOT EXISTS settings (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      key TEXT NOT NULL,
      value TEXT,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
      UNIQUE(user_id, key)
    )`);

    // API tokens table
    db.run(`CREATE TABLE IF NOT EXISTS api_tokens (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      token TEXT UNIQUE NOT NULL,
      name TEXT,
      expires_at DATETIME,
      usage_count INTEGER DEFAULT 0,
      usage_limit INTEGER,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Channel logos cache table
    db.run(`CREATE TABLE IF NOT EXISTS logo_cache (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      channel_name TEXT UNIQUE NOT NULL,
      logo_url TEXT,
      local_path TEXT,
      last_updated DATETIME DEFAULT CURRENT_TIMESTAMP
    )`);

    // M3U files library table
    db.run(`CREATE TABLE IF NOT EXISTS m3u_files (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      original_filename TEXT NOT NULL,
      file_path TEXT NOT NULL,
      size INTEGER NOT NULL,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Create VPN configs table for multiple configuration files
    db.run(`CREATE TABLE IF NOT EXISTS vpn_configs (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      user_id INTEGER NOT NULL,
      name TEXT NOT NULL,
      config_type TEXT NOT NULL CHECK(config_type IN ('openvpn', 'wireguard')),
      config_data TEXT NOT NULL,
      country TEXT,
      server_name TEXT,
      endpoint TEXT,
      is_active BOOLEAN DEFAULT 0,
      created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    )`);

    // Create indexes for performance
    db.run('CREATE INDEX IF NOT EXISTS idx_channels_playlist ON channels(playlist_id)');
    db.run('CREATE INDEX IF NOT EXISTS idx_channels_tvg_id ON channels(tvg_id)');
    db.run('CREATE INDEX IF NOT EXISTS idx_watch_history_user ON watch_history(user_id, profile_id)');
    db.run('CREATE INDEX IF NOT EXISTS idx_favorites_user ON favorites(user_id, profile_id)');
    db.run('CREATE INDEX IF NOT EXISTS idx_recordings_user ON recordings(user_id, profile_id)');
    db.run('CREATE INDEX IF NOT EXISTS idx_m3u_files_user ON m3u_files(user_id)');

    logger.info('Database tables initialized');
  });
};

module.exports = {
  db,
  initialize
};
