'use strict';

require('dotenv').config();

const express     = require('express');
const helmet      = require('helmet');
const cors        = require('cors');
const compression = require('compression');
const morgan      = require('morgan');
const rateLimit   = require('express-rate-limit');
const cron        = require('node-cron');
const winston     = require('winston');

const { testConnection } = require('./config/db');

// ── Logger ────────────────────────────────────────────────────────
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.combine(
        winston.format.colorize(),
        winston.format.simple()
      ),
    }),
    ...(process.env.LOG_FILE ? [new winston.transports.File({ filename: process.env.LOG_FILE })] : []),
  ],
});
global.logger = logger;

// ── App ───────────────────────────────────────────────────────────
const app  = express();
const PORT = parseInt(process.env.PORT || '3000');
const VER  = process.env.API_VERSION || 'v1';

// ── Security headers ──────────────────────────────────────────────
app.use(helmet({
  crossOriginResourcePolicy: { policy: 'cross-origin' },
  contentSecurityPolicy: false, // Disabled — API only
}));

// ── CORS ──────────────────────────────────────────────────────────
const allowedOrigins = (process.env.CORS_ORIGINS || 'https://chainpay.store')
  .split(',')
  .map(o => o.trim());

app.use(cors({
  origin: (origin, cb) => {
    // Allow no-origin requests (mobile apps, Postman, WP plugin)
    if (!origin) return cb(null, true);
    if (allowedOrigins.includes(origin) || origin.endsWith('.chainpay.store')) {
      return cb(null, true);
    }
    return cb(new Error(`CORS: Origin ${origin} not allowed.`));
  },
  credentials:     true,
  methods:         ['GET','POST','PUT','PATCH','DELETE','OPTIONS'],
  allowedHeaders:  ['Content-Type','Authorization','X-API-Key','X-ChainPay-Signature'],
  exposedHeaders:  ['X-Total-Count','X-Request-Id'],
}));

// ── Body parsing ──────────────────────────────────────────────────
// Keep raw body for webhook signature verification
app.use((req, res, next) => {
  if (req.path.includes('/webhook')) {
    express.raw({ type: 'application/json' })(req, res, next);
  } else {
    express.json({ limit: '10mb' })(req, res, next);
  }
});
app.use(express.urlencoded({ extended: true }));

// ── Compression ───────────────────────────────────────────────────
app.use(compression());

// ── Logging ───────────────────────────────────────────────────────
app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev'));

// ── Global rate limiting ──────────────────────────────────────────
const globalLimiter = rateLimit({
  windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'),
  max:      parseInt(process.env.RATE_LIMIT_MAX_REQUESTS || '100'),
  standardHeaders: true,
  legacyHeaders:   false,
  message: { error: 'Too many requests. Please slow down.' },
  skip: (req) => req.method === 'GET' && req.path === '/health',
});
app.use(globalLimiter);

// ── Request ID ────────────────────────────────────────────────────
app.use((req, res, next) => {
  req.id = require('crypto').randomBytes(8).toString('hex');
  res.setHeader('X-Request-Id', req.id);
  next();
});

// ── Health check (no auth) ────────────────────────────────────────
app.get('/health', (req, res) => {
  res.json({
    status:  'ok',
    service: 'ChainPay API',
    domain:  'api.chainpay.store',
    version: VER,
    env:     process.env.NODE_ENV || 'development',
    uptime:  Math.floor(process.uptime()),
    ts:      new Date().toISOString(),
  });
});

// ── Ping ──────────────────────────────────────────────────────────
app.get(`/api/${VER}/ping`, (req, res) => {
  res.json({ ok: true, message: 'ChainPay API is online.', ts: Date.now() });
});

// ── Routes ────────────────────────────────────────────────────────
const BASE = `/api/${VER}`;

app.use(`${BASE}/auth`,     require('./routes/auth'));
app.use(`${BASE}/licenses`, require('./routes/licenses'));
app.use(`${BASE}/invoices`, require('./routes/invoices'));
app.use(`${BASE}/admin`,    require('./routes/admin'));

const {
  walletRouter,
  webhookRouter,
  settingsRouter,
  pricesRouter,
  apiKeyRouter,
  storeRouter,
} = require('./routes/various');

app.use(`${BASE}/wallets`,  walletRouter);
app.use(`${BASE}/webhooks`, webhookRouter);
app.use(`${BASE}/settings`, settingsRouter);
app.use(`${BASE}/prices`,   pricesRouter);
app.use(`${BASE}/api-keys`, apiKeyRouter);
app.use(`${BASE}/stores`,   storeRouter);

// ── Plugin auto-update endpoint ───────────────────────────────────
app.get(`${BASE}/plugin/update-check`, (req, res) => {
  const { plugin_version = '0' } = req.query;
  const LATEST = '1.0.1';
  const hasUpdate = plugin_version !== LATEST;

  res.json({
    version:   LATEST,
    url:       `https://chainpay.store/chainpay-payment-gateway.zip`,
    requires:  '5.8',
    tested:    '6.7',
    requires_php: '7.4',
    sections: {
      changelog: '<h4>v1.0.1</h4><ul><li>Self-contained checkout page (all CSS/JS inline)</li><li>Fixed payment page styling on all WP themes</li><li>Updated domain to chainpay.store</li></ul>',
    },
    has_update: hasUpdate,
  });
});

// ── Plugin IPN endpoint (receives webhooks from plugin) ───────────
app.post(`${BASE}/plugin/ipn`, async (req, res) => {
  // Process WooCommerce plugin-sent payment notifications
  const { event, invoice_id, tx_hash, confirmations, api_key } = req.body;
  res.json({ received: true });
});

// ── 404 Handler ───────────────────────────────────────────────────
app.use((req, res) => {
  res.status(404).json({
    error:   'Endpoint not found.',
    path:    req.path,
    method:  req.method,
    docs:    'https://chainpay.store/docs',
  });
});

// ── Global error handler ──────────────────────────────────────────
app.use((err, req, res, next) => {
  logger.error({
    message:    err.message,
    stack:      err.stack,
    path:       req.path,
    method:     req.method,
    request_id: req.id,
  });

  if (err.message && err.message.includes('CORS')) {
    return res.status(403).json({ error: err.message });
  }

  return res.status(err.status || 500).json({
    error:      process.env.NODE_ENV === 'production' ? 'Internal server error.' : err.message,
    request_id: req.id,
  });
});

// ── CRON Jobs ─────────────────────────────────────────────────────
function startCronJobs() {
  const { refreshAll } = require('./services/prices');
  const { query } = require('./config/db');

  // Refresh all coin prices every 5 minutes
  cron.schedule('*/5 * * * *', async () => {
    try {
      await refreshAll('USD');
      logger.info('[CRON] Price feed refreshed.');
    } catch (err) {
      logger.warn('[CRON] Price refresh failed:', err.message);
    }
  });

  // Expire pending invoices every minute
  cron.schedule('* * * * *', async () => {
    try {
      const expired = await query(
        "SELECT id, user_id FROM invoices WHERE status = 'pending' AND expires_at < UTC_TIMESTAMP()"
      );
      if (expired.length > 0) {
        await query(
          "UPDATE invoices SET status = 'expired' WHERE status = 'pending' AND expires_at < UTC_TIMESTAMP()"
        );
        logger.info(`[CRON] Expired ${expired.length} invoices.`);

        // Fire expiry webhooks
        const { deliverWebhook } = require('./services/webhook');
        for (const inv of expired) {
          await deliverWebhook(inv.user_id, {
            event:      'invoice.expired',
            invoice_id: inv.id,
          }).catch(() => {});
        }
      }
    } catch (err) {
      logger.warn('[CRON] Invoice expiry failed:', err.message);
    }
  });

  // Expire licenses every hour
  cron.schedule('0 * * * *', async () => {
    try {
      const expired = await query(
        "SELECT id, user_id FROM licenses WHERE status = 'active' AND expires_at < NOW()"
      );
      if (expired.length > 0) {
        await query("UPDATE licenses SET status = 'expired' WHERE status = 'active' AND expires_at < NOW()");
        for (const lic of expired) {
          if (lic.user_id) {
            await query("UPDATE users SET plan = 'free' WHERE id = ?", [lic.user_id]);
          }
        }
        logger.info(`[CRON] Expired ${expired.length} licenses.`);
      }
    } catch (err) {
      logger.warn('[CRON] License expiry check failed:', err.message);
    }
  });

  logger.info('[CRON] All jobs scheduled.');
}

// ── Start ─────────────────────────────────────────────────────────
async function start() {
  await testConnection();

  app.listen(PORT, () => {
    logger.info(`
╔══════════════════════════════════════════════════════╗
║         ChainPay API Server — chainpay.store         ║
╠══════════════════════════════════════════════════════╣
║  URL:  http://localhost:${PORT}/api/${VER}                ║
║  Docs: https://chainpay.store/docs                   ║
║  Env:  ${(process.env.NODE_ENV || 'development').padEnd(44)} ║
╚══════════════════════════════════════════════════════╝`
    );
  });

  startCronJobs();
}

start().catch((err) => {
  logger.error('Failed to start server:', err);
  process.exit(1);
});

// ── Graceful shutdown ─────────────────────────────────────────────
process.on('SIGTERM', () => {
  logger.info('[SERVER] SIGTERM received — shutting down gracefully.');
  process.exit(0);
});
process.on('SIGINT', () => {
  logger.info('[SERVER] SIGINT received — shutting down.');
  process.exit(0);
});
process.on('unhandledRejection', (err) => {
  logger.error('[SERVER] Unhandled rejection:', err);
});

module.exports = app;
