const { pool } = require('../config/database')

class SecurityController {
  // Get failed login attempts within specified hours (default: 24h)
  static async getFailedLogins (req, res, next) {
    try {
      const { hours = 24 } = req.query
      const organizationId = req.user.organization_id
      const userRole = req.user.role

      // Validate hours parameter
      const hoursInt = parseInt(hours)
      if (isNaN(hoursInt) || hoursInt < 1 || hoursInt > 720) {
        return res.status(400).json({
          status: 'error',
          message: 'Hours must be between 1 and 720 (30 days)'
        })
      }

      let query
      let params

      // Super_admin and Admin can see all failed logins in their organization
      if (userRole === 'Super_admin' || userRole === 'Admin') {
        query = `
          SELECT 
            fla.id,
            fla.login_id,
            fla.organization_id,
            fla.ip_address,
            fla.user_agent,
            fla.attempted_at,
            fla.reason,
            u.name as user_name,
            u.email as user_email,
            o.name as organization_name
          FROM failed_login_attempts fla
          LEFT JOIN users u ON fla.login_id = u.login_id
          LEFT JOIN organizations o ON fla.organization_id = o.id
          WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
            AND (fla.organization_id = ? OR fla.organization_id IS NULL)
          ORDER BY fla.attempted_at DESC
          LIMIT 1000
        `
        params = [hoursInt, organizationId]
      } else {
        // Regular users can only see their own failed login attempts
        query = `
          SELECT 
            fla.id,
            fla.login_id,
            fla.organization_id,
            fla.ip_address,
            fla.user_agent,
            fla.attempted_at,
            fla.reason
          FROM failed_login_attempts fla
          WHERE fla.login_id = ?
            AND fla.organization_id = ?
            AND fla.attempted_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
          ORDER BY fla.attempted_at DESC
          LIMIT 100
        `
        params = [req.user.login_id, organizationId, hoursInt]
      }

      const [rows] = await pool.execute(query, params)

      // Get summary statistics
      const summaryQuery = `
        SELECT 
          COUNT(*) as total_attempts,
          COUNT(DISTINCT fla.login_id) as unique_users,
          COUNT(DISTINCT fla.ip_address) as unique_ips,
          DATE_FORMAT(MIN(fla.attempted_at), '%Y-%m-%d %H:%i:%s') as first_attempt,
          DATE_FORMAT(MAX(fla.attempted_at), '%Y-%m-%d %H:%i:%s') as last_attempt
        FROM failed_login_attempts fla
        WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
          ${userRole === 'Super_admin' || userRole === 'Admin'
            ? 'AND (fla.organization_id = ? OR fla.organization_id IS NULL)'
            : 'AND fla.login_id = ? AND fla.organization_id = ?'}
      `

      const summaryParams = userRole === 'Super_admin' || userRole === 'Admin'
        ? [hoursInt, organizationId]
        : [hoursInt, req.user.login_id, organizationId]

      const [summaryRows] = await pool.execute(summaryQuery, summaryParams)

      res.status(200).json({
        status: 'success',
        data: {
          attempts: rows,
          summary: summaryRows[0],
          period_hours: hoursInt,
          timestamp: new Date().toISOString()
        }
      })
    } catch (error) {
      next(error)
    }
  }

  // Get password changes within specified days (default: 7d)
  static async getPasswordChanges (req, res, next) {
    try {
      const { days = 7 } = req.query
      const organizationId = req.user.organization_id
      const userRole = req.user.role
      const userId = req.user.id

      // Validate days parameter
      const daysInt = parseInt(days)
      if (isNaN(daysInt) || daysInt < 1 || daysInt > 365) {
        return res.status(400).json({
          status: 'error',
          message: 'Days must be between 1 and 365'
        })
      }

      let query
      let params

      // Super_admin and Admin can see all password changes in their organization
      if (userRole === 'Super_admin' || userRole === 'Admin') {
        query = `
          SELECT 
            pch.id,
            pch.user_id,
            pch.changed_by,
            pch.organization_id,
            pch.ip_address,
            pch.user_agent,
            pch.changed_at,
            pch.change_type,
            u.name as user_name,
            u.email as user_email,
            u.login_id,
            cb.name as changed_by_name,
            cb.email as changed_by_email,
            o.name as organization_name
          FROM password_change_history pch
          INNER JOIN users u ON pch.user_id = u.id
          INNER JOIN users cb ON pch.changed_by = cb.id
          LEFT JOIN organizations o ON pch.organization_id = o.id
          WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
            AND pch.organization_id = ?
          ORDER BY pch.changed_at DESC
          LIMIT 1000
        `
        params = [daysInt, organizationId]
      } else {
        // Regular users can only see their own password changes
        query = `
          SELECT 
            pch.id,
            pch.user_id,
            pch.changed_by,
            pch.organization_id,
            pch.ip_address,
            pch.changed_at,
            pch.change_type,
            cb.name as changed_by_name
          FROM password_change_history pch
          INNER JOIN users cb ON pch.changed_by = cb.id
          WHERE pch.user_id = ?
            AND pch.organization_id = ?
            AND pch.changed_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
          ORDER BY pch.changed_at DESC
          LIMIT 100
        `
        params = [userId, organizationId, daysInt]
      }

      const [rows] = await pool.execute(query, params)

      // Get summary statistics
      const summaryQuery = `
        SELECT 
          COUNT(*) as total_changes,
          COUNT(DISTINCT pch.user_id) as unique_users,
          COUNT(CASE WHEN pch.change_type = 'self' THEN 1 END) as self_changes,
          COUNT(CASE WHEN pch.change_type = 'admin_reset' THEN 1 END) as admin_resets,
          COUNT(CASE WHEN pch.change_type = 'forced' THEN 1 END) as forced_changes,
          DATE_FORMAT(MIN(pch.changed_at), '%Y-%m-%d %H:%i:%s') as first_change,
          DATE_FORMAT(MAX(pch.changed_at), '%Y-%m-%d %H:%i:%s') as last_change
        FROM password_change_history pch
        WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
          ${userRole === 'Super_admin' || userRole === 'Admin'
            ? 'AND pch.organization_id = ?'
            : 'AND pch.user_id = ? AND pch.organization_id = ?'}
      `

      const summaryParams = userRole === 'Super_admin' || userRole === 'Admin'
        ? [daysInt, organizationId]
        : [daysInt, userId, organizationId]

      const [summaryRows] = await pool.execute(summaryQuery, summaryParams)

      res.status(200).json({
        status: 'success',
        data: {
          changes: rows,
          summary: summaryRows[0],
          period_days: daysInt,
          timestamp: new Date().toISOString()
        }
      })
    } catch (error) {
      next(error)
    }
  }

  // Get security overview (combines failed logins and password changes)
  static async getSecurityOverview (req, res, next) {
    try {
      const organizationId = req.user.organization_id
      const userRole = req.user.role
      const userId = req.user.id

      const [failedLogins24h, passwordChanges7d, recentSecurityEvents] = await Promise.all([
        SecurityController.getFailedLoginsSummary(organizationId, userRole, userId, 24),
        SecurityController.getPasswordChangesSummary(organizationId, userRole, userId, 7),
        SecurityController.getRecentSecurityEvents(organizationId, userRole, userId, 10)
      ])

      res.status(200).json({
        status: 'success',
        data: {
          failed_logins_24h: failedLogins24h,
          password_changes_7d: passwordChanges7d,
          recent_events: recentSecurityEvents,
          timestamp: new Date().toISOString()
        }
      })
    } catch (error) {
      next(error)
    }
  }

  // Helper method: Get failed logins summary
  static async getFailedLoginsSummary (organizationId, userRole, userId, hours = 24) {
    try {
      let query
      let params

      if (userRole === 'Super_admin' || userRole === 'Admin') {
        query = `
          SELECT 
            COUNT(*) as total_attempts,
            COUNT(DISTINCT fla.login_id) as unique_users,
            COUNT(DISTINCT fla.ip_address) as unique_ips
          FROM failed_login_attempts fla
          WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
            AND (fla.organization_id = ? OR fla.organization_id IS NULL)
        `
        params = [hours, organizationId]
      } else {
        query = `
          SELECT 
            COUNT(*) as total_attempts,
            1 as unique_users,
            COUNT(DISTINCT fla.ip_address) as unique_ips
          FROM failed_login_attempts fla
          WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL ? HOUR)
            AND fla.login_id = (SELECT login_id FROM users WHERE id = ?)
            AND fla.organization_id = ?
        `
        params = [hours, userId, organizationId]
      }

      const [rows] = await pool.execute(query, params)
      return rows[0]
    } catch (error) {
      throw new Error(`Error getting failed logins summary: ${error.message}`)
    }
  }

  // Helper method: Get password changes summary
  static async getPasswordChangesSummary (organizationId, userRole, userId, days = 7) {
    try {
      let query
      let params

      if (userRole === 'Super_admin' || userRole === 'Admin') {
        query = `
          SELECT 
            COUNT(*) as total_changes,
            COUNT(DISTINCT pch.user_id) as unique_users,
            COUNT(CASE WHEN pch.change_type = 'self' THEN 1 END) as self_changes,
            COUNT(CASE WHEN pch.change_type = 'admin_reset' THEN 1 END) as admin_resets
          FROM password_change_history pch
          WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
            AND pch.organization_id = ?
        `
        params = [days, organizationId]
      } else {
        query = `
          SELECT 
            COUNT(*) as total_changes,
            1 as unique_users,
            COUNT(CASE WHEN pch.change_type = 'self' THEN 1 END) as self_changes,
            COUNT(CASE WHEN pch.change_type = 'admin_reset' THEN 1 END) as admin_resets
          FROM password_change_history pch
          WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
            AND pch.user_id = ?
            AND pch.organization_id = ?
        `
        params = [days, userId, organizationId]
      }

      const [rows] = await pool.execute(query, params)
      return rows[0]
    } catch (error) {
      throw new Error(`Error getting password changes summary: ${error.message}`)
    }
  }

  // Helper method: Get recent security events
  static async getRecentSecurityEvents (organizationId, userRole, userId, limit = 10) {
    try {
      // Ensure limit is a valid integer (for security, prevent SQL injection)
      const limitInt = Math.max(1, Math.min(parseInt(limit) || 10, 100))

      let query
      let params

      if (userRole === 'Super_admin' || userRole === 'Admin') {
        query = `
          SELECT * FROM (
            SELECT 
              'failed_login' as event_type,
              fla.login_id as user_identifier,
              u.name as user_name,
              fla.ip_address,
              fla.attempted_at as event_time,
              fla.reason as details
            FROM failed_login_attempts fla
            LEFT JOIN users u ON fla.login_id = u.login_id
            WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
              AND fla.organization_id = ?
            UNION ALL
            SELECT 
              'password_change' as event_type,
              u.login_id as user_identifier,
              u.name as user_name,
              pch.ip_address,
              pch.changed_at as event_time,
              pch.change_type as details
            FROM password_change_history pch
            INNER JOIN users u ON pch.user_id = u.id
            WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
              AND pch.organization_id = ?
          ) AS combined_events
          ORDER BY event_time DESC
          LIMIT ${limitInt}
        `
        params = [organizationId, organizationId]
      } else {
        query = `
          SELECT * FROM (
            SELECT 
              'failed_login' as event_type,
              fla.login_id as user_identifier,
              NULL as user_name,
              fla.ip_address,
              fla.attempted_at as event_time,
              fla.reason as details
            FROM failed_login_attempts fla
            INNER JOIN users u ON fla.login_id = u.login_id
            WHERE fla.attempted_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
              AND u.id = ?
              AND fla.organization_id = ?
            UNION ALL
            SELECT 
              'password_change' as event_type,
              u.login_id as user_identifier,
              NULL as user_name,
              pch.ip_address,
              pch.changed_at as event_time,
              pch.change_type as details
            FROM password_change_history pch
            INNER JOIN users u ON pch.user_id = u.id
            WHERE pch.changed_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
              AND pch.user_id = ?
              AND pch.organization_id = ?
          ) AS combined_events
          ORDER BY event_time DESC
          LIMIT ${limitInt}
        `
        params = [userId, organizationId, userId, organizationId]
      }

      const [rows] = await pool.execute(query, params)
      return rows
    } catch (error) {
      console.error('Error getting recent security events:', error)
      throw new Error(`Error getting recent security events: ${error.message}`)
    }
  }

  // Log failed login attempt (helper method to be called from authController)
  static async logFailedLoginAttempt (loginId, organizationId, ipAddress, userAgent, reason = 'Invalid credentials') {
    try {
      const query = `
        INSERT INTO failed_login_attempts (login_id, organization_id, ip_address, user_agent, reason, attempted_at)
        VALUES (?, ?, ?, ?, ?, NOW())
      `
      await pool.execute(query, [loginId, organizationId, ipAddress, userAgent, reason])
    } catch (error) {
      // Don't throw error - logging failures shouldn't break the auth flow
      console.error('Error logging failed login attempt:', error)
    }
  }

  // Log password change (helper method to be called from authController)
  static async logPasswordChange (userId, changedBy, organizationId, ipAddress, userAgent, changeType = 'self') {
    try {
      const query = `
        INSERT INTO password_change_history (user_id, changed_by, organization_id, ip_address, user_agent, change_type, changed_at)
        VALUES (?, ?, ?, ?, ?, ?, NOW())
      `
      await pool.execute(query, [userId, changedBy, organizationId, ipAddress, userAgent, changeType])
    } catch (error) {
      // Don't throw error - logging failures shouldn't break the auth flow
      console.error('Error logging password change:', error)
    }
  }
}

module.exports = SecurityController
