import { Router } from 'express';
import crypto from 'crypto';
import { authenticate } from '../../../middleware/auth.middleware';
import { requireRole } from '../../../middleware/rbac.middleware';
import { siteSettingsService } from '../../settings/settings.service';
import { termuxClient, TermuxClient } from '../../../services/termux.client';
import { tailscaleClient, TailscaleClient } from '../../../services/tailscale.client';
import { logger } from '../../../utils/logger';
import rateLimit from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';
import { redis } from '../../../config/redis';

const router = Router();

// Rate limit: 10 per 5 minutes (same as Pangolin setup)
const setupRateLimit = rateLimit({
  windowMs: 5 * 60 * 1000,
  max: 10,
  standardHeaders: true,
  legacyHeaders: false,
  store: new RedisStore({
    sendCommand: (command: string, ...args: string[]) => redis.call(command, ...args) as Promise<any>,
    prefix: 'rl:sms-setup:',
  }),
  message: {
    error: {
      message: 'Too many SMS setup requests, please try again later',
      code: 'SMS_SETUP_RATE_LIMIT_EXCEEDED',
    },
  },
});

// All routes require SUPER_ADMIN (secrets management)
router.use(authenticate, requireRole('SUPER_ADMIN'), setupRateLimit);

/**
 * GET /api/sms/setup/status
 * Quick status check — is SMS configured and connected?
 */
router.get('/status', async (_req, res) => {
  try {
    const connected = termuxClient.enabled ? await termuxClient.isAvailable() : false;

    res.json({
      configured: termuxClient.enabled,
      connected,
      tailscaleConfigured: tailscaleClient.configured,
      source: termuxClient.configSource,
    });
  } catch (err) {
    logger.warn('SMS setup status check failed:', err);
    res.status(500).json({ error: { message: 'Failed to check SMS status', code: 'INTERNAL_ERROR' } });
  }
});

/**
 * GET /api/sms/setup/config
 * Return current SMS config (admin-only).
 * Masks API key values for display.
 */
router.get('/config', async (_req, res) => {
  try {
    const settings = await siteSettingsService.get();

    res.json({
      enableSms: settings.enableSms,
      smsTermuxApiUrl: settings.smsTermuxApiUrl || '',
      smsTermuxApiKey: settings.smsTermuxApiKey ? '••••••••' : '',
      smsTermuxApiKeySet: !!settings.smsTermuxApiKey,
      smsTailscaleApiKey: settings.smsTailscaleApiKey ? '••••••••' : '',
      smsTailscaleApiKeySet: !!settings.smsTailscaleApiKey,
      smsTailscaleTailnet: settings.smsTailscaleTailnet || '',
      smsTailscaleDeviceId: settings.smsTailscaleDeviceId || '',
      smsTailscaleDeviceName: settings.smsTailscaleDeviceName || '',
    });
  } catch (err) {
    logger.warn('SMS setup config fetch failed:', err);
    res.status(500).json({ error: { message: 'Failed to fetch SMS config', code: 'INTERNAL_ERROR' } });
  }
});

/**
 * POST /api/sms/setup/tailscale/devices
 * Query the Tailscale API for devices. Temporarily configures the client.
 * Body: { apiKey: string, tailnet?: string }
 */
router.post('/tailscale/devices', async (req, res) => {
  try {
    const { apiKey, tailnet } = req.body as { apiKey?: string; tailnet?: string };

    if (!apiKey) {
      res.status(400).json({ error: { message: 'apiKey is required', code: 'VALIDATION_ERROR' } });
      return;
    }

    // Use a throwaway instance to avoid mutating the shared singleton
    const tmp = new TailscaleClient();
    tmp.configure(apiKey, tailnet || undefined);

    const devices = await tmp.listDevices();

    // Sort: Android first, then by online status, then by name
    const sorted = [...devices].sort((a, b) => {
      const aAndroid = a.os.toLowerCase() === 'android' ? 0 : 1;
      const bAndroid = b.os.toLowerCase() === 'android' ? 0 : 1;
      if (aAndroid !== bAndroid) return aAndroid - bAndroid;
      if (a.online !== b.online) return a.online ? -1 : 1;
      return a.name.localeCompare(b.name);
    });

    res.json({ devices: sorted });
  } catch (err) {
    const msg = err instanceof Error ? err.message : 'Failed to query Tailscale API';
    logger.warn('Tailscale device discovery failed:', msg);
    res.status(502).json({ error: { message: msg, code: 'TAILSCALE_ERROR' } });
  }
});

/**
 * POST /api/sms/setup/test-connection
 * Test Termux connectivity with arbitrary URL + key (does not use stored config).
 * Body: { url: string, apiKey: string }
 */
router.post('/test-connection', async (req, res) => {
  try {
    const { url, apiKey } = req.body as { url?: string; apiKey?: string };

    if (!url || !apiKey) {
      res.status(400).json({ error: { message: 'url and apiKey are required', code: 'VALIDATION_ERROR' } });
      return;
    }

    // Validate URL format and protocol
    let parsedUrl: URL;
    try {
      parsedUrl = new URL(url);
    } catch {
      res.status(400).json({ error: { message: 'Invalid URL format', code: 'VALIDATION_ERROR' } });
      return;
    }

    if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
      res.status(400).json({ error: { message: 'Only http and https URLs are allowed', code: 'VALIDATION_ERROR' } });
      return;
    }

    const result = await TermuxClient.testConnection(url, apiKey);
    res.json(result);
  } catch (err) {
    const msg = err instanceof Error ? err.message : 'Connection test failed';
    res.status(500).json({ error: { message: msg, code: 'INTERNAL_ERROR' } });
  }
});

/**
 * POST /api/sms/setup/save-config
 * Save all SMS config to DB, then reload termux client.
 */
router.post('/save-config', async (req, res) => {
  try {
    const {
      enableSms,
      smsTermuxApiUrl,
      smsTermuxApiKey,
      smsTailscaleApiKey,
      smsTailscaleTailnet,
      smsTailscaleDeviceId,
      smsTailscaleDeviceName,
    } = req.body as Record<string, unknown>;

    // Build update payload (only include fields that were provided)
    const update: Record<string, unknown> = {};

    if (typeof enableSms === 'boolean') update.enableSms = enableSms;
    if (typeof smsTermuxApiUrl === 'string') update.smsTermuxApiUrl = smsTermuxApiUrl;
    if (typeof smsTermuxApiKey === 'string') update.smsTermuxApiKey = smsTermuxApiKey;
    if (typeof smsTailscaleApiKey === 'string') update.smsTailscaleApiKey = smsTailscaleApiKey;
    if (typeof smsTailscaleTailnet === 'string') update.smsTailscaleTailnet = smsTailscaleTailnet;
    if (typeof smsTailscaleDeviceId === 'string') update.smsTailscaleDeviceId = smsTailscaleDeviceId;
    if (typeof smsTailscaleDeviceName === 'string') update.smsTailscaleDeviceName = smsTailscaleDeviceName;

    // Validate URL format and protocol if provided
    if (typeof smsTermuxApiUrl === 'string' && smsTermuxApiUrl) {
      try {
        const parsed = new URL(smsTermuxApiUrl);
        if (!['http:', 'https:'].includes(parsed.protocol)) {
          res.status(400).json({ error: { message: 'Only http and https URLs are allowed', code: 'VALIDATION_ERROR' } });
          return;
        }
      } catch {
        res.status(400).json({ error: { message: 'Invalid Termux API URL format', code: 'VALIDATION_ERROR' } });
        return;
      }
    }

    if (Object.keys(update).length === 0) {
      res.status(400).json({ error: { message: 'No fields to update', code: 'VALIDATION_ERROR' } });
      return;
    }

    await siteSettingsService.update(update);

    // Hot-reload the termux client with new config
    await termuxClient.configureFromDb();

    // Also reload tailscale client if tailscale key was provided
    if (typeof smsTailscaleApiKey === 'string' || typeof smsTailscaleTailnet === 'string') {
      await tailscaleClient.configureFromDb();
    }

    logger.info(`SMS config updated by ${req.user?.email}`);

    res.json({ success: true, message: 'SMS configuration saved' });
  } catch (err) {
    logger.error('SMS config save failed:', err);
    res.status(500).json({ error: { message: 'Failed to save SMS configuration', code: 'INTERNAL_ERROR' } });
  }
});

/**
 * POST /api/sms/setup/generate-key
 * Generate a 64-character hex API key for Termux server auth.
 */
router.post('/generate-key', (_req, res) => {
  const key = crypto.randomBytes(32).toString('hex');
  res.json({ key });
});

export const smsSetupRouter = router;
