require('dotenv').config();
const express = require('express');
const cors = require('cors');
const compression = require('compression');
const { initializeDatabase } = require('./db');
const { verifyToken, register, login, refreshAccessToken, revokeRefreshToken } = require('./auth');
const {
  fetchStockPrice,
  fetchMultipleStocks,
  getAllStocksFromDatabase,
  initializeStockData,
  DEFAULT_SYMBOLS,
  shutdown,
  flushBatchToDatabase,
} = require('./stockService');

const app = express();
const PORT = process.env.PORT || 3000;
const INSTANCE_NAME = process.env.INSTANCE_NAME || 'backend-instance';

// Middleware
app.use(cors());
app.use(express.json());

// Compression middleware for better throughput
app.use(compression({
  level: 6, // Balanced compression level
  threshold: 1024, // Only compress responses > 1KB
  filter: (req, res) => {
    if (req.headers['x-no-compression']) {
      return false;
    }
    return compression.filter(req, res);
  },
}));

// Request logging middleware - only log non-health checks in production
app.use((req, res, next) => {
  if (req.path !== '/health' || process.env.NODE_ENV !== 'production') {
    console.log(
      `[${INSTANCE_NAME}] ${new Date().toISOString()} - ${req.method} ${req.path}`,
    );
  }
  next();
});

// Health check endpoint (for Traefik)
app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    instance: INSTANCE_NAME,
    timestamp: new Date().toISOString(),
  });
});

// Public endpoint - Get instance info
app.get('/api/instance', (req, res) => {
  res.json({
    instance: INSTANCE_NAME,
    timestamp: new Date().toISOString(),
  });
});

// Auth endpoints
app.post('/api/auth/register', async (req, res) => {
  try {
    const { username, password } = req.body;

    if (!username || !password) {
      return res
        .status(400)
        .json({ error: 'Username and password are required' });
    }

    if (password.length < 6) {
      return res
        .status(400)
        .json({ error: 'Password must be at least 6 characters' });
    }

    const result = await register(username, password);
    res.json({
      message: 'User registered successfully',
      user: result.user,
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      instance: INSTANCE_NAME,
    });
  } catch (error) {
    console.error('Registration error:', error);
    res.status(400).json({ error: error.message });
  }
});

app.post('/api/auth/login', async (req, res) => {
  try {
    const { username, password } = req.body;

    if (!username || !password) {
      return res
        .status(400)
        .json({ error: 'Username and password are required' });
    }

    const result = await login(username, password);
    res.json({
      message: 'Login successful',
      user: result.user,
      accessToken: result.accessToken,
      refreshToken: result.refreshToken,
      instance: INSTANCE_NAME,
    });
  } catch (error) {
    console.error('Login error:', error);
    res.status(401).json({ error: error.message });
  }
});

// Refresh token endpoint
app.post('/api/auth/refresh', async (req, res) => {
  try {
    const { refreshToken } = req.body;

    if (!refreshToken) {
      return res.status(400).json({ error: 'Refresh token is required' });
    }

    const result = await refreshAccessToken(refreshToken);
    res.json({
      message: 'Token refreshed successfully',
      accessToken: result.accessToken,
      user: result.user,
      instance: INSTANCE_NAME,
    });
  } catch (error) {
    console.error('Token refresh error:', error);
    res.status(401).json({ error: error.message });
  }
});

// Logout endpoint (revoke refresh token)
app.post('/api/auth/logout', async (req, res) => {
  try {
    const { refreshToken } = req.body;

    if (refreshToken) {
      await revokeRefreshToken(refreshToken);
    }

    res.json({
      message: 'Logged out successfully',
      instance: INSTANCE_NAME,
    });
  } catch (error) {
    console.error('Logout error:', error);
    res.status(500).json({ error: 'Logout failed' });
  }
});

// Protected endpoints (require JWT)
app.get('/api/stocks', verifyToken, async (req, res) => {
  try {
    console.log(
      `[${INSTANCE_NAME}] Fetching stocks for user: ${req.user.username}`,
    );

    // Fetch fresh data
    const stocks = await fetchMultipleStocks(DEFAULT_SYMBOLS);

    res.json({
      stocks,
      instance: INSTANCE_NAME,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Error fetching stocks:', error);
    res.status(500).json({ error: 'Failed to fetch stock data' });
  }
});

app.get('/api/stocks/:symbol', verifyToken, async (req, res) => {
  try {
    const { symbol } = req.params;
    console.log(
      `[${INSTANCE_NAME}] Fetching stock ${symbol} for user: ${req.user.username}`,
    );

    const stock = await fetchStockPrice(symbol.toUpperCase());

    res.json({
      stock,
      instance: INSTANCE_NAME,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Error fetching stock:', error);
    res.status(500).json({ error: 'Failed to fetch stock data' });
  }
});

// Get cached stocks from database (optimized for maximum throughput)
app.get('/api/stocks/cached/all', verifyToken, async (req, res) => {
  try {
    const stocks = await getAllStocksFromDatabase();

    res.json({
      stocks,
      instance: INSTANCE_NAME,
      cached: true,
      timestamp: new Date().toISOString(),
    });
  } catch (error) {
    console.error('Error fetching cached stocks:', error);
    res.status(500).json({ error: 'Failed to fetch cached stock data' });
  }
});

// Heartbeat endpoint for monitoring
app.get('/api/heartbeat', verifyToken, (req, res) => {
  res.json({
    status: 'alive',
    instance: INSTANCE_NAME,
    user: req.user.username,
    timestamp: new Date().toISOString(),
  });
});

// Error handling middleware
app.use((err, req, res, next) => {
  console.error('Unhandled error:', err);
  res.status(500).json({ error: 'Internal server error' });
});

// Initialize and start server
async function startServer() {
  try {
    console.log(`[${INSTANCE_NAME}] Initializing database...`);
    await initializeDatabase();

    console.log(`[${INSTANCE_NAME}] Initializing stock data...`);
    await initializeStockData();

    app.listen(PORT, '0.0.0.0', () => {
      console.log(`[${INSTANCE_NAME}] Server running on port ${PORT}`);
      console.log(
        `[${INSTANCE_NAME}] Health check: http://localhost:${PORT}/health`,
      );
    });

    // Background refresh using setImmediate to avoid blocking event loop
    let refreshInterval;
    const refreshStockData = async () => {
      try {
        console.log(`[${INSTANCE_NAME}] Refreshing stock data...`);
        await fetchMultipleStocks(DEFAULT_SYMBOLS);
        // Flush any pending batch writes
        await flushBatchToDatabase();
      } catch (error) {
        console.error(`[${INSTANCE_NAME}] Error refreshing stock data:`, error);
      }
      // Schedule next refresh without blocking
      refreshInterval = setTimeout(refreshStockData, 60000);
    };

    // Start background refresh after initial delay
    setTimeout(refreshStockData, 10000);

    // Graceful shutdown handling
    const gracefulShutdown = async (signal) => {
      console.log(`\n[${INSTANCE_NAME}] ${signal} received, shutting down gracefully...`);
      clearTimeout(refreshInterval);

      try {
        await shutdown();
        console.log(`[${INSTANCE_NAME}] Cleanup completed`);
        process.exit(0);
      } catch (error) {
        console.error(`[${INSTANCE_NAME}] Error during shutdown:`, error);
        process.exit(1);
      }
    };

    process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
    process.on('SIGINT', () => gracefulShutdown('SIGINT'));
  } catch (error) {
    console.error('Failed to start server:', error);
    process.exit(1);
  }
}

startServer();

// Made with Bob
