import { Request, Response } from 'express';
import path from 'path';
import { z } from 'zod';
import nextcloudService from '../services/nextcloud.service';
import userService from '../services/user.service';
import logger from '../utils/logger';

const PollRequestSchema = z.object({
  pollEndpoint: z.string().url(),
  pollToken: z.string().min(1),
});

class NextcloudController {
  async initiateConnect(_req: Request, res: Response): Promise<void> {
    try {
      const data = await nextcloudService.initiateLoginFlow();
      res.status(200).json({ success: true, data });
    } catch (error) {
      logger.error('initiateConnect error', { error });
      res.status(500).json({ success: false, message: 'Nextcloud-Verbindung konnte nicht gestartet werden' });
    }
  }

  async pollConnect(req: Request, res: Response): Promise<void> {
    try {
      const parsed = PollRequestSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      const result = await nextcloudService.pollLoginFlow(parsed.data.pollEndpoint, parsed.data.pollToken);

      if (!result) {
        res.status(200).json({ success: true, data: { completed: false } });
        return;
      }

      await userService.updateNextcloudCredentials(req.user!.id, result.loginName, result.appPassword);
      res.status(200).json({ success: true, data: { completed: true } });
    } catch (error) {
      logger.error('pollConnect error', { error });
      res.status(500).json({ success: false, message: 'Nextcloud-Abfrage fehlgeschlagen' });
    }
  }

  async getConversations(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }

      const { totalUnread, conversations } = await nextcloudService.getConversations(
        credentials.loginName,
        credentials.appPassword,
      );
      res.status(200).json({ success: true, data: { connected: true, totalUnread, conversations } });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }
      logger.error('getConversations error', { error });
      res.status(500).json({ success: false, message: 'Nextcloud-Gespräche konnten nicht geladen werden' });
    }
  }

  async disconnect(req: Request, res: Response): Promise<void> {
    try {
      await userService.clearNextcloudCredentials(req.user!.id);
      res.status(200).json({ success: true, data: null });
    } catch (error) {
      logger.error('disconnect error', { error });
      res.status(500).json({ success: false, message: 'Nextcloud-Trennung fehlgeschlagen' });
    }
  }

  async getRooms(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(200).json({ success: true, data: { connected: false, rooms: [] } });
        return;
      }
      const rooms = await nextcloudService.getAllConversations(credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: { connected: true, rooms, loginName: credentials.loginName } });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false, rooms: [] } });
        return;
      }
      logger.error('getRooms error', { error });
      res.status(500).json({ success: false, message: 'Nextcloud-Räume konnten nicht geladen werden' });
    }
  }

  async getMessages(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const token = req.params.token as string;
      if (!token) {
        res.status(400).json({ success: false, message: 'Room token fehlt' });
        return;
      }
      const lookIntoFuture = req.query.lookIntoFuture === '1';
      const lastKnownMessageId = req.query.lastKnownMessageId
        ? parseInt(req.query.lastKnownMessageId as string, 10)
        : undefined;
      const timeout = req.query.timeout
        ? Math.min(parseInt(req.query.timeout as string, 10), 25)
        : 25;
      const messages = await nextcloudService.getMessages(token, credentials.loginName, credentials.appPassword, {
        lookIntoFuture,
        lastKnownMessageId,
        timeout,
      });
      res.status(200).json({ success: true, data: messages });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }
      logger.error('getMessages error', { error });
      res.status(500).json({ success: false, message: 'Nachrichten konnten nicht geladen werden' });
    }
  }

  async sendMessage(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const token = req.params.token as string;
      const { message, replyTo } = req.body;
      if (!token || !message || typeof message !== 'string' || message.trim().length === 0) {
        res.status(400).json({ success: false, message: 'Token und Nachricht erforderlich' });
        return;
      }
      if (message.length > 32000) {
        res.status(400).json({ success: false, message: 'Nachricht zu lang' });
        return;
      }
      const replyToNum = (typeof replyTo === 'number' && replyTo > 0) ? replyTo : undefined;
      await nextcloudService.sendMessage(token, message.trim(), credentials.loginName, credentials.appPassword, replyToNum);
      res.status(200).json({ success: true, data: null });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }
      logger.error('sendMessage error', { error });
      res.status(500).json({ success: false, message: 'Nachricht konnte nicht gesendet werden' });
    }
  }

  async uploadFile(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const token = req.params.token as string;
      if (!token) {
        res.status(400).json({ success: false, message: 'Room token fehlt' });
        return;
      }
      if (!req.file) {
        res.status(400).json({ success: false, message: 'Keine Datei übermittelt' });
        return;
      }
      await nextcloudService.uploadFileToTalk(
        token,
        req.file.buffer,
        req.file.originalname,
        req.file.mimetype,
        credentials.loginName,
        credentials.appPassword,
      );
      res.status(200).json({ success: true, data: null });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }
      logger.error('uploadFile error', { error });
      res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
    }
  }

  async downloadFile(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const filePath = req.query.path as string;
      if (!filePath) {
        res.status(400).json({ success: false, message: 'Dateipfad fehlt' });
        return;
      }
      // Path traversal protection
      const normalized = path.normalize(filePath);
      if (normalized.includes('..') || !normalized.startsWith('/')) {
        res.status(400).json({ success: false, message: 'Ungültiger Dateipfad' });
        return;
      }
      const response = await nextcloudService.downloadFile(
        filePath,
        credentials.loginName,
        credentials.appPassword,
      );
      const contentType = response.headers['content-type'] ?? 'application/octet-stream';
      const contentDisposition = response.headers['content-disposition']
        ?? `attachment; filename="${String(req.params.fileId).replace(/["\r\n\\]/g, '_')}"`;
      res.setHeader('Content-Type', contentType);
      res.setHeader('Content-Disposition', contentDisposition);
      if (response.headers['content-length']) {
        res.setHeader('Content-Length', response.headers['content-length']);
      }
      response.data.pipe(res);
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      logger.error('downloadFile error', { error });
      res.status(500).json({ success: false, message: 'Datei konnte nicht heruntergeladen werden' });
    }
  }

  async getFilePreview(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const fileId = parseInt(req.params.fileId as string, 10);
      if (isNaN(fileId)) {
        res.status(400).json({ success: false, message: 'Ungültige Datei-ID' });
        return;
      }
      const w = parseInt((req.query.w as string) ?? '400', 10) || 400;
      const h = parseInt((req.query.h as string) ?? '400', 10) || 400;
      const response = await nextcloudService.getFilePreview(
        fileId,
        Math.min(w, 1200),
        Math.min(h, 1200),
        credentials.loginName,
        credentials.appPassword,
      );
      const contentType = response.headers['content-type'] ?? 'image/jpeg';
      res.setHeader('Content-Type', contentType);
      if (response.headers['content-length']) {
        res.setHeader('Content-Length', response.headers['content-length']);
      }
      res.setHeader('Cache-Control', 'private, max-age=300');
      response.data.pipe(res);
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      logger.error('getFilePreview error', { error });
      res.status(500).json({ success: false, message: 'Vorschau konnte nicht geladen werden' });
    }
  }

  async markRoomAsRead(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const token = req.params.token as string;
      if (!token) {
        res.status(400).json({ success: false, message: 'Room token fehlt' });
        return;
      }
      await nextcloudService.markAsRead(token, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: null });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: { connected: false } });
        return;
      }
      logger.error('markRoomAsRead error', { error });
      res.status(500).json({ success: false, message: 'Raum konnte nicht als gelesen markiert werden' });
    }
  }

  async searchUsers(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(200).json({ success: true, data: [] });
        return;
      }
      const query = (req.query.search as string) ?? '';
      const results = await nextcloudService.searchUsers(query, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: results });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(200).json({ success: true, data: [] });
        return;
      }
      logger.error('searchUsers error', { error });
      res.status(500).json({ success: false, message: 'Benutzersuche fehlgeschlagen' });
    }
  }

  async createRoom(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) {
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      const { roomType, invite, roomName } = req.body;
      if (typeof roomType !== 'number' || !invite || typeof invite !== 'string') {
        res.status(400).json({ success: false, message: 'roomType und invite erforderlich' });
        return;
      }
      const result = await nextcloudService.createRoom(roomType, invite, roomName, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: result });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') {
        await userService.clearNextcloudCredentials(req.user!.id);
        res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' });
        return;
      }
      logger.error('createRoom error', { error });
      res.status(500).json({ success: false, message: 'Raum konnte nicht erstellt werden' });
    }
  }

  async addReaction(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) { res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      const token = req.params.token as string;
      const messageId = parseInt(req.params.messageId as string, 10);
      const { reaction } = req.body;
      if (!token || isNaN(messageId) || !reaction) { res.status(400).json({ success: false, message: 'Parameter fehlen' }); return; }
      await nextcloudService.addReaction(token, messageId, reaction, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: null });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') { await userService.clearNextcloudCredentials(req.user!.id); res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      logger.error('addReaction error', { error });
      res.status(500).json({ success: false, message: 'Reaktion konnte nicht hinzugefügt werden' });
    }
  }

  async removeReaction(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) { res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      const token = req.params.token as string;
      const messageId = parseInt(req.params.messageId as string, 10);
      const reaction = req.query.reaction as string;
      if (!token || isNaN(messageId) || !reaction) { res.status(400).json({ success: false, message: 'Parameter fehlen' }); return; }
      await nextcloudService.removeReaction(token, messageId, reaction, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data: null });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') { await userService.clearNextcloudCredentials(req.user!.id); res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      logger.error('removeReaction error', { error });
      res.status(500).json({ success: false, message: 'Reaktion konnte nicht entfernt werden' });
    }
  }

  async getReactions(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) { res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      const token = req.params.token as string;
      const messageId = parseInt(req.params.messageId as string, 10);
      if (!token || isNaN(messageId)) { res.status(400).json({ success: false, message: 'Parameter fehlen' }); return; }
      const data = await nextcloudService.getReactions(token, messageId, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') { await userService.clearNextcloudCredentials(req.user!.id); res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      logger.error('getReactions error', { error });
      res.status(500).json({ success: false, message: 'Reaktionen konnten nicht geladen werden' });
    }
  }

  async getPoll(req: Request, res: Response): Promise<void> {
    try {
      const credentials = await userService.getNextcloudCredentials(req.user!.id);
      if (!credentials) { res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      const token = req.params.token as string;
      const pollId = parseInt(req.params.pollId as string, 10);
      if (!token || isNaN(pollId)) { res.status(400).json({ success: false, message: 'Parameter fehlen' }); return; }
      const data = await nextcloudService.getPollDetails(token, pollId, credentials.loginName, credentials.appPassword);
      res.status(200).json({ success: true, data });
    } catch (error: any) {
      if (error?.code === 'NEXTCLOUD_AUTH_INVALID') { await userService.clearNextcloudCredentials(req.user!.id); res.status(401).json({ success: false, message: 'Nextcloud nicht verbunden' }); return; }
      logger.error('getPoll error', { error });
      res.status(500).json({ success: false, message: 'Abstimmung konnte nicht geladen werden' });
    }
  }
}

export default new NextcloudController();
