/**
 * Synor Public API Gateway
 *
 * Provides rate-limited, authenticated access to Synor blockchain RPC.
 *
 * Endpoints:
 * - POST /rpc - JSON-RPC proxy to node (requires API key)
 * - GET /health - Health check
 * - GET /v1/keys - List API keys (admin)
 * - POST /v1/keys - Create API key (admin)
 * - GET /v1/keys/:key/stats - Get usage stats
 * - DELETE /v1/keys/:key - Revoke API key (admin)
 * - GET /v1/analytics - Global analytics (admin)
 */

import express, { Request, Response, NextFunction } from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';
import helmet from 'helmet';
import cors from 'cors';
import { RateLimiter, RateLimitTier } from './rate-limiter.js';
import { ApiKeyManager } from './api-keys.js';

// Configuration
const PORT = parseInt(process.env.PORT || '3100');
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
const RPC_TARGET = process.env.RPC_TARGET || 'http://localhost:16110';
const ADMIN_KEY = process.env.ADMIN_KEY || 'admin-secret-key';

// Initialize services
const rateLimiter = new RateLimiter(REDIS_URL);
const apiKeyManager = new ApiKeyManager(REDIS_URL);

const app = express();

// Security middleware
app.use(helmet());
app.use(cors());
app.use(express.json());

// Request timing middleware
app.use((req: Request, res: Response, next: NextFunction) => {
  (req as any).startTime = Date.now();
  next();
});

// Health check (no auth required)
app.get('/health', (req: Request, res: Response) => {
  res.json({ status: 'healthy', timestamp: Date.now() });
});

// API key extraction middleware
const extractApiKey = (req: Request): string | null => {
  // Check Authorization header
  const authHeader = req.headers.authorization;
  if (authHeader?.startsWith('Bearer ')) {
    return authHeader.substring(7);
  }

  // Check X-API-Key header
  const apiKeyHeader = req.headers['x-api-key'];
  if (typeof apiKeyHeader === 'string') {
    return apiKeyHeader;
  }

  // Check query parameter
  if (typeof req.query.api_key === 'string') {
    return req.query.api_key;
  }

  return null;
};

// Rate limiting and auth middleware for RPC
const rpcAuthMiddleware = async (
  req: Request,
  res: Response,
  next: NextFunction
) => {
  const apiKey = extractApiKey(req);

  let tier: RateLimitTier = 'free';
  let keyIdentifier: string;

  if (apiKey) {
    const keyData = await apiKeyManager.validateKey(apiKey);
    if (keyData) {
      tier = keyData.tier;
      keyIdentifier = apiKey;
    } else {
      return res.status(401).json({
        jsonrpc: '2.0',
        error: { code: -32001, message: 'Invalid API key' },
        id: null,
      });
    }
  } else {
    // Anonymous access - rate limit by IP
    keyIdentifier = `ip:${req.ip}`;
  }

  // Check rate limit
  const result = await rateLimiter.checkLimit(keyIdentifier, tier);

  // Set rate limit headers
  res.set('X-RateLimit-Limit', tier === 'enterprise' ? 'unlimited' : String(
    tier === 'developer' ? 1000 : 100
  ));
  res.set('X-RateLimit-Remaining', String(result.remaining));
  res.set('X-RateLimit-Reset', String(Math.ceil(result.resetAt / 1000)));
  res.set('X-RateLimit-Tier', tier);

  if (!result.allowed) {
    return res.status(429).json({
      jsonrpc: '2.0',
      error: {
        code: -32005,
        message: 'Rate limit exceeded',
        data: {
          retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),
          tier,
          upgrade: tier === 'free'
            ? 'Upgrade to Developer tier for 1000 req/min'
            : tier === 'developer'
            ? 'Contact us for Enterprise tier'
            : null,
        },
      },
      id: null,
    });
  }

  // Store for analytics
  (req as any).apiKey = keyIdentifier;
  (req as any).tier = tier;

  next();
};

// RPC proxy
app.post(
  '/rpc',
  rpcAuthMiddleware,
  createProxyMiddleware({
    target: RPC_TARGET,
    changeOrigin: true,
    pathRewrite: { '^/rpc': '/' },
    onProxyRes: async (proxyRes, req: any) => {
      // Record usage analytics
      const responseTime = Date.now() - req.startTime;
      const body = req.body || {};
      const method = body.method || 'unknown';

      await apiKeyManager.recordUsage(req.apiKey, method, responseTime);
    },
    onError: (err, req, res: any) => {
      res.status(502).json({
        jsonrpc: '2.0',
        error: { code: -32603, message: 'RPC node unavailable' },
        id: null,
      });
    },
  })
);

// Admin middleware
const adminAuth = (req: Request, res: Response, next: NextFunction) => {
  const authHeader = req.headers.authorization;
  if (authHeader !== `Bearer ${ADMIN_KEY}`) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  next();
};

// Admin: Create API key
app.post('/v1/keys', adminAuth, async (req: Request, res: Response) => {
  const { tier, name } = req.body;

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

  if (!['free', 'developer', 'enterprise'].includes(tier)) {
    return res.status(400).json({ error: 'Invalid tier' });
  }

  const key = await apiKeyManager.createKey({ tier, name });
  res.status(201).json(key);
});

// Admin: List API keys
app.get('/v1/keys', adminAuth, async (req: Request, res: Response) => {
  const keys = await apiKeyManager.listKeys();
  res.json({ keys });
});

// Get API key stats (key owner or admin)
app.get('/v1/keys/:key/stats', async (req: Request, res: Response) => {
  const { key } = req.params;
  const authHeader = req.headers.authorization;

  // Allow key owner or admin
  if (authHeader !== `Bearer ${key}` && authHeader !== `Bearer ${ADMIN_KEY}`) {
    return res.status(403).json({ error: 'Forbidden' });
  }

  const stats = await apiKeyManager.getKeyWithStats(key);
  if (!stats) {
    return res.status(404).json({ error: 'Key not found' });
  }

  const analytics = await apiKeyManager.getAnalytics(key);
  res.json({ ...stats, analytics });
});

// Admin: Revoke API key
app.delete('/v1/keys/:key', adminAuth, async (req: Request, res: Response) => {
  const { key } = req.params;
  const deleted = await apiKeyManager.revokeKey(key);

  if (!deleted) {
    return res.status(404).json({ error: 'Key not found' });
  }

  res.json({ success: true });
});

// Admin: Global analytics
app.get('/v1/analytics', adminAuth, async (req: Request, res: Response) => {
  // Get global statistics
  res.json({
    message: 'Analytics endpoint - implement as needed',
    timestamp: Date.now(),
  });
});

// Error handler
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error('Error:', err.message);
  res.status(500).json({
    jsonrpc: '2.0',
    error: { code: -32603, message: 'Internal server error' },
    id: null,
  });
});

// Start server
app.listen(PORT, () => {
  console.log(`Synor API Gateway running on port ${PORT}`);
  console.log(`Proxying RPC to: ${RPC_TARGET}`);
});

// Graceful shutdown
process.on('SIGTERM', async () => {
  console.log('Shutting down...');
  await rateLimiter.close();
  await apiKeyManager.close();
  process.exit(0);
});
