import { Request, Response } from 'express';
import trainingService from '../services/training.service';
import {
  CreateUebungSchema,
  UpdateUebungSchema,
  UpdateRsvpSchema,
  MarkAttendanceSchema,
  CancelEventSchema,
} from '../models/training.model';
import logger from '../utils/logger';
import environment from '../config/environment';

class TrainingController {
  // -------------------------------------------------------------------------
  // GET /api/training
  // -------------------------------------------------------------------------
  getUpcoming = async (req: Request, res: Response): Promise<void> => {
    try {
      const limit = Math.min(Number(req.query.limit ?? 10), 50);
      const userId = req.user?.id;
      const events = await trainingService.getUpcomingEvents(limit, userId);

      res.json({ success: true, data: events });
    } catch (error) {
      logger.error('getUpcoming error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Laden der Veranstaltungen' });
    }
  };

  // -------------------------------------------------------------------------
  // GET /api/training/calendar?from=&to=
  // -------------------------------------------------------------------------
  getCalendarRange = async (req: Request, res: Response): Promise<void> => {
    try {
      const fromStr = req.query.from as string | undefined;
      const toStr   = req.query.to   as string | undefined;

      if (!fromStr || !toStr) {
        res.status(400).json({
          success: false,
          message: 'Query-Parameter "from" und "to" sind erforderlich (ISO-8601)',
        });
        return;
      }

      const from = new Date(fromStr);
      const to   = new Date(toStr);

      if (isNaN(from.getTime()) || isNaN(to.getTime())) {
        res.status(400).json({ success: false, message: 'Ungültiges Datumsformat' });
        return;
      }

      if (to < from) {
        res.status(400).json({ success: false, message: '"to" muss nach "from" liegen' });
        return;
      }

      const userId = req.user?.id;
      const events = await trainingService.getEventsByDateRange(from, to, userId);
      res.json({ success: true, data: events });
    } catch (error) {
      logger.error('getCalendarRange error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Laden des Kalenders' });
    }
  };

  // -------------------------------------------------------------------------
  // GET /api/training/calendar.ics?token=<calendarToken>
  //
  // Auth design: We use a per-user opaque token stored in `calendar_tokens`.
  // This allows calendar clients (Apple Calendar, Google Calendar) to subscribe
  // without sending a Bearer token on every refresh. The token is long-lived
  // and can be rotated by calling POST /api/training/calendar-token/rotate.
  //
  // TRADEOFF DISCUSSION:
  //   Option A (used here) — Personal calendar token:
  //     + Works natively in all calendar apps (no auth headers needed)
  //     + Rotating the token invalidates stale subscriptions
  //     - Token embedded in URL is visible in server logs / browser history
  //     → Mitigation: tokens are 256-bit random, never contain PII
  //
  //   Option B — Bearer auth only:
  //     + No token in URL
  //     - Most native calendar apps cannot send custom headers;
  //       requires a proxy or special client
  //     → Rejected for this use case (firefighter mobile phones)
  //
  //   Option C — Publicly readable feed (no auth):
  //     + Simplest
  //     - Leaks member names and attendance status to anyone with the URL
  //     → Rejected on privacy grounds
  // -------------------------------------------------------------------------
  getIcalExport = async (req: Request, res: Response): Promise<void> => {
    try {
      const token = req.query.token as string | undefined;

      let userId: string | undefined;

      if (token) {
        const resolved = await trainingService.resolveCalendarToken(token);
        if (!resolved) {
          res.status(401).json({ success: false, message: 'Ungültiger Kalender-Token' });
          return;
        }
        userId = resolved;
      } else if (req.user) {
        // Also accept a normal Bearer token for direct browser access
        userId = req.user.id;
      }
      // userId may be undefined → returns all events without personal RSVP info

      const ics = await trainingService.getCalendarExport(userId);

      res.setHeader('Content-Type', 'text/calendar; charset=utf-8');
      res.setHeader('Content-Disposition', 'attachment; filename="feuerwehr-rems.ics"');
      // 30-minute cache — calendar clients typically re-fetch at this interval
      res.setHeader('Cache-Control', 'max-age=1800, public');
      res.send(ics);
    } catch (error) {
      logger.error('getIcalExport error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Erstellen des Kalender-Exports' });
    }
  };

  // -------------------------------------------------------------------------
  // GET /api/training/calendar-token  (authenticated — get or create token)
  // -------------------------------------------------------------------------
  getCalendarToken = async (req: Request, res: Response): Promise<void> => {
    try {
      if (!req.user) {
        res.status(401).json({ success: false, message: 'Nicht authentifiziert' });
        return;
      }

      const token = await trainingService.getOrCreateCalendarToken(req.user.id);
      const baseUrl = environment.cors.origin.replace(/\/$/, '');
      const subscribeUrl = `${baseUrl.replace('localhost:3001', 'localhost:3000')}/api/training/calendar.ics?token=${token}`;

      res.json({
        success: true,
        data: {
          token,
          subscribeUrl,
          instructions: 'Kopiere diese URL und füge sie in "Kalender abonnieren" ein (Apple Kalender, Google Calendar, Thunderbird, etc.).',
        },
      });
    } catch (error) {
      logger.error('getCalendarToken error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Laden des Kalender-Tokens' });
    }
  };

  // -------------------------------------------------------------------------
  // GET /api/training/stats?year=2026
  // -------------------------------------------------------------------------
  getStats = async (req: Request, res: Response): Promise<void> => {
    try {
      const year = Number(req.query.year ?? new Date().getFullYear());

      if (isNaN(year) || year < 2000 || year > 2100) {
        res.status(400).json({ success: false, message: 'Ungültiges Jahr' });
        return;
      }

      const stats = await trainingService.getMemberParticipationStats(year);
      res.json({ success: true, data: stats, meta: { year } });
    } catch (error) {
      logger.error('getStats error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Laden der Statistiken' });
    }
  };

  // -------------------------------------------------------------------------
  // GET /api/training/:id
  // -------------------------------------------------------------------------
  getById = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id } = req.params as Record<string, string>;
      const userId = req.user?.id;

      // Determine if the requester may see the full attendee list
      // In v1 we use a simple check: if the user has training:write permission
      // (Kommandant / Gruppenführer), populate teilnahmen.
      // The permission flag is set on req by requirePermission middleware,
      // but we check it here by convention.
      const canSeeTeilnahmen = (req as any).canSeeTeilnahmen === true;

      const event = await trainingService.getEventById(id, userId, canSeeTeilnahmen);

      if (!event) {
        res.status(404).json({ success: false, message: 'Veranstaltung nicht gefunden' });
        return;
      }

      res.json({ success: true, data: event });
    } catch (error) {
      logger.error('getById error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Laden der Veranstaltung' });
    }
  };

  // -------------------------------------------------------------------------
  // POST /api/training
  // -------------------------------------------------------------------------
  createEvent = async (req: Request, res: Response): Promise<void> => {
    try {
      const parsed = CreateUebungSchema.safeParse(req.body);

      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      const event = await trainingService.createEvent(parsed.data, req.user!.id);
      res.status(201).json({ success: true, data: event });
    } catch (error) {
      logger.error('createEvent error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Erstellen der Veranstaltung' });
    }
  };

  // -------------------------------------------------------------------------
  // PATCH /api/training/:id
  // -------------------------------------------------------------------------
  updateEvent = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id } = req.params as Record<string, string>;
      const parsed = UpdateUebungSchema.safeParse(req.body);

      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      const event = await trainingService.updateEvent(id, parsed.data, req.user!.id);
      res.json({ success: true, data: event });
    } catch (error: any) {
      if (error.message === 'Event not found') {
        res.status(404).json({ success: false, message: 'Veranstaltung nicht gefunden' });
        return;
      }
      logger.error('updateEvent error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Aktualisieren der Veranstaltung' });
    }
  };

  // -------------------------------------------------------------------------
  // DELETE /api/training/:id  (soft cancel)
  // -------------------------------------------------------------------------
  cancelEvent = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id } = req.params as Record<string, string>;
      const parsed = CancelEventSchema.safeParse(req.body);

      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      await trainingService.cancelEvent(id, parsed.data.absage_grund, req.user!.id);
      res.json({ success: true, message: 'Veranstaltung wurde abgesagt' });
    } catch (error: any) {
      if (error.message === 'Event not found') {
        res.status(404).json({ success: false, message: 'Veranstaltung nicht gefunden' });
        return;
      }
      logger.error('cancelEvent error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Absagen der Veranstaltung' });
    }
  };

  // -------------------------------------------------------------------------
  // PATCH /api/training/:id/attendance  — own RSVP
  // -------------------------------------------------------------------------
  updateRsvp = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id: uebungId } = req.params as Record<string, string>;
      const userId = req.user!.id;
      const parsed = UpdateRsvpSchema.safeParse(req.body);

      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      await trainingService.updateAttendanceRSVP(
        uebungId,
        userId,
        parsed.data.status,
        parsed.data.bemerkung
      );

      res.json({ success: true, message: 'Rückmeldung gespeichert' });
    } catch (error) {
      logger.error('updateRsvp error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Speichern der Rückmeldung' });
    }
  };

  // -------------------------------------------------------------------------
  // POST /api/training/:id/attendance/mark  — bulk mark as erschienen
  // -------------------------------------------------------------------------
  markAttendance = async (req: Request, res: Response): Promise<void> => {
    try {
      const { id: uebungId } = req.params as Record<string, string>;
      const parsed = MarkAttendanceSchema.safeParse(req.body);

      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }

      await trainingService.markAttendance(uebungId, parsed.data.userIds, req.user!.id);
      res.json({
        success: true,
        message: `${parsed.data.userIds.length} Personen als erschienen markiert`,
      });
    } catch (error) {
      logger.error('markAttendance error', { error });
      res.status(500).json({ success: false, message: 'Fehler beim Erfassen der Anwesenheit' });
    }
  };
}

export default new TrainingController();
