import { Request, Response } from 'express';
import httpClient from '../config/httpClient';
import toolConfigService from '../services/toolConfig.service';
import settingsService from '../services/settings.service';
import logger from '../utils/logger';

const TOOL_SETTINGS_KEYS: Record<string, string> = {
  bookstack: 'tool_config_bookstack',
  vikunja: 'tool_config_vikunja',
  nextcloud: 'tool_config_nextcloud',
};

const MASKED_FIELDS = ['tokenSecret', 'apiToken', 'bot_app_password'];

function maskValue(value: string): string {
  if (!value || value.length <= 4) return '****';
  return '*'.repeat(value.length - 4) + value.slice(-4);
}

function maskConfig(config: Record<string, string>): Record<string, string> {
  const result: Record<string, string> = {};
  for (const [k, v] of Object.entries(config)) {
    result[k] = (MASKED_FIELDS.includes(k) && v) ? maskValue(v) : v;
  }
  return result;
}

/**
 * Validates that a URL is safe to use as an outbound service endpoint.
 * Rejects non-http(s) protocols and private/loopback IP ranges to prevent SSRF.
 */
function isValidServiceUrl(raw: string): boolean {
  let parsed: URL;
  try {
    parsed = new URL(raw);
  } catch {
    return false;
  }

  if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
    return false;
  }

  const hostname = parsed.hostname.toLowerCase();

  if (hostname === 'localhost' || hostname === '::1') {
    return false;
  }

  const ipv4Parts = hostname.split('.');
  if (ipv4Parts.length === 4) {
    const [a, b] = ipv4Parts.map(Number);
    if (
      a === 127 ||
      a === 10 ||
      (a === 172 && b >= 16 && b <= 31) ||
      (a === 192 && b === 168) ||
      (a === 169 && b === 254)
    ) {
      return false;
    }
  }

  return true;
}

class ToolConfigController {
  async getConfig(req: Request, res: Response): Promise<void> {
    const tool = req.params.tool as string;
    if (!TOOL_SETTINGS_KEYS[tool]) {
      res.status(400).json({ success: false, message: 'Unbekanntes Tool' });
      return;
    }

    try {
      let config: Record<string, string>;
      if (tool === 'bookstack') {
        config = await toolConfigService.getBookstackConfig() as unknown as Record<string, string>;
      } else if (tool === 'vikunja') {
        config = await toolConfigService.getVikunjaConfig() as unknown as Record<string, string>;
      } else {
        config = await toolConfigService.getNextcloudConfig() as unknown as Record<string, string>;
      }
      res.status(200).json({ success: true, data: maskConfig(config) });
    } catch (error) {
      logger.error('ToolConfigController.getConfig error', { error, tool });
      res.status(500).json({ success: false, message: 'Konfiguration konnte nicht geladen werden' });
    }
  }

  async updateConfig(req: Request, res: Response): Promise<void> {
    const tool = req.params.tool as string;
    const settingsKey = TOOL_SETTINGS_KEYS[tool];
    if (!settingsKey) {
      res.status(400).json({ success: false, message: 'Unbekanntes Tool' });
      return;
    }

    try {
      const userId = req.user!.id;
      const existing = await settingsService.get(settingsKey);
      const current = existing?.value && typeof existing.value === 'object' ? existing.value as Record<string, unknown> : {};
      const incoming: Record<string, unknown> = { ...req.body };
      for (const key of Object.keys(incoming)) {
        if (typeof incoming[key] === 'string' && /^\*+/.test(incoming[key] as string)) {
          delete incoming[key];
        }
      }
      const merged = { ...current, ...incoming };
      await settingsService.set(settingsKey, merged, userId);
      toolConfigService.clearCache();
      res.status(200).json({ success: true, message: 'Konfiguration gespeichert' });
    } catch (error) {
      logger.error('ToolConfigController.updateConfig error', { error, tool });
      res.status(500).json({ success: false, message: 'Konfiguration konnte nicht gespeichert werden' });
    }
  }

  async testConnection(req: Request, res: Response): Promise<void> {
    const tool = req.params.tool as string;
    if (!TOOL_SETTINGS_KEYS[tool]) {
      res.status(400).json({ success: false, message: 'Unbekanntes Tool' });
      return;
    }

    try {
      let url: string;
      let requestConfig: { headers?: Record<string, string> } = {};

      if (tool === 'bookstack') {
        const current = await toolConfigService.getBookstackConfig();
        const merged = { ...current, ...req.body };
        url = merged.url;
        if (!url || !isValidServiceUrl(url)) {
          res.status(200).json({ success: false, message: 'Ungültige URL', latencyMs: 0 });
          return;
        }
        requestConfig.headers = {
          'Authorization': `Token ${merged.tokenId}:${merged.tokenSecret}`,
          'Content-Type': 'application/json',
        };
        url = `${url}/api/books?count=1`;
      } else if (tool === 'vikunja') {
        const current = await toolConfigService.getVikunjaConfig();
        const merged = { ...current, ...req.body };
        url = merged.url;
        if (!url || !isValidServiceUrl(url)) {
          res.status(200).json({ success: false, message: 'Ungültige URL', latencyMs: 0 });
          return;
        }
        requestConfig.headers = {
          'Authorization': `Bearer ${merged.apiToken}`,
          'Content-Type': 'application/json',
        };
        url = `${url}/api/v1/info`;
      } else {
        const current = await toolConfigService.getNextcloudConfig();
        const merged = { ...current, ...req.body };
        url = merged.url;
        if (!url || !isValidServiceUrl(url)) {
          res.status(200).json({ success: false, message: 'Ungültige URL', latencyMs: 0 });
          return;
        }
        url = `${url}/status.php`;
      }

      const start = Date.now();
      await httpClient.get(url, { ...requestConfig, timeout: 10_000 });
      const latencyMs = Date.now() - start;

      res.status(200).json({ success: true, message: 'Verbindung erfolgreich', latencyMs });
    } catch (error: any) {
      const latencyMs = 0;
      const status = error?.response?.status;
      const message = status
        ? `Verbindung fehlgeschlagen (HTTP ${status})`
        : 'Verbindung fehlgeschlagen';
      logger.error('ToolConfigController.testConnection error', { error, tool });
      res.status(200).json({ success: false, message, latencyMs });
    }
  }
}

export default new ToolConfigController();
