/**
 * Tailscale admin & settings routes
 *
 * Two distinct surfaces, both gated by DashCaddy's TOTP auth:
 *
 *   GET    /api/v1/tailscale/settings
 *     Returns { configured, tailnetName, deviceCount, keyValidatedAt }
 *     NEVER returns the raw API token.
 *
 *   PUT    /api/v1/tailscale/settings
 *     body: { apiToken: 'tskey-api-...' }
 *     Validates by pinging /api/v2/tailnet/-/preferences. On success,
 *     stores the token encrypted and writes tailscale-config.json metadata.
 *     Returns the same shape as GET (without the token).
 *
 *   DELETE /api/v1/tailscale/settings
 *     Clears the stored token and metadata.
 *
 *   POST   /api/v1/tailscale/settings/test
 *     body: { apiToken?: 'tskey-api-...' }   // optional; defaults to stored
 *     Pings Tailscale with the given token (or stored one) and returns
 *     { valid: bool, tailnetName?, error? }. Does NOT save anything.
 *
 *   GET    /api/v1/tailscale/admin/devices
 *     Lists all devices in the tailnet via the coord API. 503 if not configured.
 *
 *   GET    /api/v1/tailscale/admin/users
 *     Lists tailnet users.
 *
 *   GET    /api/v1/tailscale/admin/keys
 *     Lists pre-auth keys (metadata only, never the secret).
 *
 *   POST   /api/v1/tailscale/admin/keys
 *     body: { reusable?, ephemeral?, preauthorized?, tags?, description?, expirySeconds? }
 *     Creates a new pre-auth key. Returns { id, key } — the `key` is the
 *     ONLY time the secret is available, callers must show it to the user
 *     immediately and not store it.
 *
 *   DELETE /api/v1/tailscale/admin/keys/:id
 *     Revokes a pre-auth key.
 *
 *   DELETE /api/v1/tailscale/admin/devices/:id
 *     Revokes a device from the tailnet.
 */

const express = require('express');
const { ok, errorResponse } = require('../src/utils/responses');
const { TailscaleCoordError } = require('../src/managers/tailscale-coord');

module.exports = function({
  tailscaleCoord,
  asyncHandler,
  log,
  logError: _logError,
}) {
  const router = express.Router();

  // ---------- Settings ----------

  router.get('/settings', asyncHandler(
    // eslint-disable-next-line require-await
    async (req, res) => {
    const meta = tailscaleCoord.loadMetadata();
    if (!meta.configured) {
      return ok(res, { configured: false });
    }
    return ok(res, {
      configured: true,
      tailnetName: meta.tailnetName || null,
      deviceCount: typeof meta.deviceCount === 'number' ? meta.deviceCount : null,
      keyValidatedAt: meta.keyValidatedAt || null,
      lastUsedAt: meta.lastUsedAt || null,
    });
  }));

  router.put('/settings', asyncHandler(async (req, res) => {
    const token = req.body && req.body.apiToken;
    if (!token || typeof token !== 'string' || !token.startsWith('tskey-api-')) {
      return errorResponse(res, 400, 'Invalid API token (must start with tskey-api-)');
    }

    // Validate before storing
    const client = new (require('../src/managers/tailscale-coord').TailscaleCoordClient)({ apiToken: token });
    let prefs;
    try {
      prefs = await client.ping();
    } catch (e) {
      if (e instanceof TailscaleCoordError) {
        if (e.code === 'unauthorized') {
          return errorResponse(res, 401, 'Tailscale rejected this API token (401 unauthorized)');
        }
        return errorResponse(res, 502, 'Tailscale API error: ' + e.message);
      }
      throw e;
    }

    // Get device count for the metadata
    let deviceCount = null;
    try {
      const devs = await client.listDevices();
      deviceCount = devs.length;
    } catch (_e) { /* non-fatal */ }

    // Persist token (encrypted) + metadata (plaintext)
    await tailscaleCoord.setApiToken(token);
    tailscaleCoord.saveMetadata({
      configured: true,
      tailnetName: prefs.domain || null,
      deviceCount,
      keyValidatedAt: new Date().toISOString(),
      lastUsedAt: new Date().toISOString(),
    });

    if (log && log.info) log.info('tailscale-coord', 'API token configured', { tailnetName: prefs.domain, deviceCount });

    return ok(res, {
      configured: true,
      tailnetName: prefs.domain || null,
      deviceCount,
      keyValidatedAt: new Date().toISOString(),
    });
  }));

  router.delete('/settings', asyncHandler(async (req, res) => {
    await tailscaleCoord.setApiToken(null);
    tailscaleCoord.saveMetadata({ configured: false });
    if (log && log.info) log.info('tailscale-coord', 'API token cleared');
    return ok(res, { configured: false });
  }));

  router.post('/settings/test', asyncHandler(async (req, res) => {
    const token = (req.body && req.body.apiToken) || null;
    const client = await tailscaleCoord.getClient();
    if (token) {
      // Caller provided a fresh token to test — don't save it
      client.setApiToken(token);
    }
    if (!client.isConfigured()) {
      return ok(res, { valid: false, error: 'No Tailscale API token configured' });
    }
    try {
      const prefs = await client.ping({ skipCache: true });
      return ok(res, { valid: true, tailnetName: prefs.domain });
    } catch (e) {
      if (e instanceof TailscaleCoordError && e.code === 'unauthorized') {
        return ok(res, { valid: false, error: 'Tailscale rejected the token (unauthorized)' });
      }
      return ok(res, { valid: false, error: e.message });
    }
  }));

  // ---------- Admin: devices ----------

  router.get('/admin/devices', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    try {
      const devices = await client.listDevices();
      return ok(res, { devices, count: devices.length });
    } catch (e) {
      if (e instanceof TailscaleCoordError && e.code === 'unauthorized') {
        return errorResponse(res, 401, 'Tailscale rejected the configured token');
      }
      throw e;
    }
  }));

  router.delete('/admin/devices/:id', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    const id = req.params.id;
    try {
      await client.deleteDevice(id);
      if (log && log.info) log.info('tailscale-coord', 'Device deleted', { deviceId: id });
      return ok(res, { success: true, deviceId: id });
    } catch (e) {
      if (e instanceof TailscaleCoordError) {
        if (e.code === 'unauthorized') return errorResponse(res, 401, 'Unauthorized');
        if (e.code === 'not_found') return errorResponse(res, 404, 'Device not found');
      }
      throw e;
    }
  }));

  // ---------- Admin: users ----------

  router.get('/admin/users', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    const users = await client.listUsers();
    return ok(res, { users, count: users.length });
  }));

  // ---------- Admin: pre-auth keys ----------

  router.get('/admin/keys', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    const keys = await client.listAuthKeys();
    return ok(res, { keys, count: keys.length });
  }));

  router.post('/admin/keys', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    const opts = req.body || {};
    // Reject obviously-bad input early
    if (opts.tags && !Array.isArray(opts.tags)) {
      return errorResponse(res, 400, 'tags must be an array of strings');
    }
    if (opts.expirySeconds !== undefined && (!Number.isInteger(opts.expirySeconds) || opts.expirySeconds <= 0)) {
      return errorResponse(res, 400, 'expirySeconds must be a positive integer');
    }
    try {
      const result = await client.createAuthKey(opts);
      if (log && log.info) log.info('tailscale-coord', 'Auth key created', { id: result.id, description: opts.description, tags: opts.tags });
      return ok(res, result);
    } catch (e) {
      if (e instanceof TailscaleCoordError) {
        if (e.code === 'unauthorized') return errorResponse(res, 401, 'Unauthorized');
        return errorResponse(res, 502, 'Tailscale API error: ' + e.message);
      }
      throw e;
    }
  }));

  router.delete('/admin/keys/:id', asyncHandler(async (req, res) => {
    const client = await tailscaleCoord.getClient();
    if (!client.isConfigured()) {
      return errorResponse(res, 503, 'Tailscale API token not configured');
    }
    const id = req.params.id;
    try {
      await client.deleteAuthKey(id);
      if (log && log.info) log.info('tailscale-coord', 'Auth key deleted', { keyId: id });
      return ok(res, { success: true, keyId: id });
    } catch (e) {
      if (e instanceof TailscaleCoordError) {
        if (e.code === 'unauthorized') return errorResponse(res, 401, 'Unauthorized');
        if (e.code === 'not_found') return errorResponse(res, 404, 'Key not found');
      }
      throw e;
    }
  }));

  return router;
};