import { Request, Response } from 'express';
import { z } from 'zod';
import vehicleService from '../services/vehicle.service';
import equipmentService from '../services/equipment.service';
import scheduledMessagesService from '../services/scheduledMessages.service';
import { FahrzeugStatus } from '../models/vehicle.model';
import logger from '../utils/logger';

// ── UUID validation ───────────────────────────────────────────────────────────

function isValidUUID(s: string): boolean {
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
}

// ── Zod Validation Schemas ────────────────────────────────────────────────────

const FahrzeugStatusEnum = z.enum([
  FahrzeugStatus.Einsatzbereit,
  FahrzeugStatus.AusserDienstWartung,
  FahrzeugStatus.AusserDienstSchaden,
]);

const isoDate = z.string().regex(
  /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
  'Erwartet ISO-Datum im Format YYYY-MM-DD'
);

const CreateFahrzeugSchema = z.object({
  bezeichnung:              z.string().min(1).max(100),
  kurzname:                 z.string().max(20).optional(),
  amtliches_kennzeichen:    z.string().max(20).optional(),
  fahrgestellnummer:        z.string().max(50).optional(),
  baujahr:                  z.number().int().min(1950).max(2100).optional(),
  hersteller:               z.string().max(100).optional(),
  typ_schluessel:           z.string().max(30).optional(),
  besatzung_soll:           z.string().max(10).optional(),
  status:                   FahrzeugStatusEnum.optional(),
  status_bemerkung:         z.string().max(500).optional(),
  standort:                 z.string().min(1).max(100).optional(),
  bild_url:                 z.string().url().max(500).refine(
    (url) => /^https?:\/\//i.test(url),
    'Nur http/https URLs erlaubt'
  ).optional(),
  paragraph57a_faellig_am:  isoDate.optional(),
  naechste_wartung_am:      isoDate.optional(),
});

const UpdateFahrzeugSchema = z.object({
  bezeichnung:              z.string().min(1).max(100).optional(),
  kurzname:                 z.string().max(20).nullable().optional(),
  amtliches_kennzeichen:    z.string().max(20).nullable().optional(),
  fahrgestellnummer:        z.string().max(50).nullable().optional(),
  baujahr:                  z.number().int().min(1950).max(2100).nullable().optional(),
  hersteller:               z.string().max(100).nullable().optional(),
  typ_schluessel:           z.string().max(30).nullable().optional(),
  besatzung_soll:           z.string().max(10).nullable().optional(),
  status:                   FahrzeugStatusEnum.optional(),
  status_bemerkung:         z.string().max(500).nullable().optional(),
  standort:                 z.string().min(1).max(100).optional(),
  bild_url:                 z.string().url().max(500).refine(
    (url) => /^https?:\/\//i.test(url),
    'Nur http/https URLs erlaubt'
  ).nullable().optional(),
  paragraph57a_faellig_am:  isoDate.nullable().optional(),
  naechste_wartung_am:      isoDate.nullable().optional(),
});

const isoDatetime = z.string().datetime({ offset: true, message: 'Erwartet ISO-8601 Datum mit Zeitzone' });

const UpdateStatusSchema = z.object({
  status:           FahrzeugStatusEnum,
  bemerkung:        z.string().max(500).optional().default(''),
  ausserDienstVon:  isoDatetime.optional(),
  ausserDienstBis:  isoDatetime.optional(),
}).refine(
  (d) => {
    const isAusserDienst = d.status === FahrzeugStatus.AusserDienstWartung || d.status === FahrzeugStatus.AusserDienstSchaden;
    if (!isAusserDienst) return true;
    return !!d.ausserDienstVon && !!d.ausserDienstBis;
  },
  { message: 'Außer-Dienst-Zeitraum (von + bis) ist bei diesem Status erforderlich', path: ['ausserDienstVon'] }
).refine(
  (d) => {
    if (!d.ausserDienstVon || !d.ausserDienstBis) return true;
    return new Date(d.ausserDienstBis) > new Date(d.ausserDienstVon);
  },
  { message: 'Enddatum muss nach Startdatum liegen', path: ['ausserDienstBis'] }
);

const ErgebnisEnum = z.enum(['bestanden', 'bestanden_mit_maengeln', 'nicht_bestanden']);

const CreateWartungslogSchema = z.object({
  datum:              isoDate,
  art:                z.enum(['§57a Prüfung', 'Service', 'Sonstiges']).optional(),
  beschreibung:       z.string().min(1).max(2000),
  km_stand:           z.number().int().min(0).optional(),
  kraftstoff_liter:   z.number().min(0).optional(),
  kosten:             z.number().min(0).optional(),
  externe_werkstatt:  z.string().max(150).optional(),
  ergebnis:           ErgebnisEnum.optional(),
  naechste_faelligkeit: isoDate.optional(),
});

const UpdateWartungslogSchema = z.object({
  datum:              isoDate,
  art:                z.enum(['§57a Prüfung', 'Service', 'Sonstiges']).optional(),
  beschreibung:       z.string().min(1).max(2000),
  km_stand:           z.number().int().min(0).optional(),
  externe_werkstatt:  z.string().max(150).optional(),
  ergebnis:           ErgebnisEnum.optional(),
  naechste_faelligkeit: isoDate.optional(),
});

// ── Helper ────────────────────────────────────────────────────────────────────

function getUserId(req: Request): string {
  return req.user!.id;
}

// ── Controller ────────────────────────────────────────────────────────────────

class VehicleController {
  async listVehicles(_req: Request, res: Response): Promise<void> {
    try {
      const vehicles = await vehicleService.getAllVehicles();
      res.status(200).json({ success: true, data: vehicles });
    } catch (error) {
      logger.error('listVehicles error', { error });
      res.status(500).json({ success: false, message: 'Fahrzeuge konnten nicht geladen werden' });
    }
  }

  async getStats(_req: Request, res: Response): Promise<void> {
    try {
      const stats = await vehicleService.getVehicleStats();
      res.status(200).json({ success: true, data: stats });
    } catch (error) {
      logger.error('getStats error', { error });
      res.status(500).json({ success: false, message: 'Statistiken konnten nicht geladen werden' });
    }
  }

  async getAlerts(req: Request, res: Response): Promise<void> {
    try {
      const raw = parseInt((req.query.daysAhead as string) || '30', 10);
      if (isNaN(raw) || raw < 0) {
        res.status(400).json({ success: false, message: 'Ungültiger daysAhead-Wert' });
        return;
      }
      const daysAhead = Math.min(raw, 365);
      const alerts = await vehicleService.getUpcomingInspections(daysAhead);
      res.status(200).json({ success: true, data: alerts });
    } catch (error) {
      logger.error('getAlerts error', { error });
      res.status(500).json({ success: false, message: 'Prüfungshinweise konnten nicht geladen werden' });
    }
  }

  async exportAlerts(_req: Request, res: Response): Promise<void> {
    try {
      const escape = (v: unknown): string => {
        if (v === null || v === undefined) return '';
        const str = String(v);
        let safe = str.replace(/"/g, '""');
        if (/^[=+@\-]/.test(safe)) safe = "'" + safe;
        return `"${safe}"`;
      };

      const formatDate = (d: Date | string | null): string => {
        if (!d) return '';
        const date = typeof d === 'string' ? new Date(d) : d;
        const day = String(date.getDate()).padStart(2, '0');
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const year = date.getFullYear();
        return `${day}.${month}.${year}`;
      };

      const [vehicleAlerts, equipmentAlerts] = await Promise.all([
        vehicleService.getUpcomingInspections(365),
        equipmentService.getUpcomingInspections(365),
      ]);

      const header = 'Typ,Bezeichnung,Kurzname,Prüfungsart,Fällig am,Tage verbleibend';
      const rows: string[] = [];

      for (const a of vehicleAlerts) {
        const pruefungsart = a.type === '57a' ? '§57a Überprüfung' : 'Nächste Wartung';
        rows.push([
          escape('Fahrzeug'),
          escape(a.bezeichnung),
          escape(a.kurzname),
          escape(pruefungsart),
          escape(formatDate(a.faelligAm)),
          escape(a.tage),
        ].join(','));
      }

      for (const e of equipmentAlerts) {
        rows.push([
          escape('Ausrüstung'),
          escape(e.bezeichnung),
          escape(''),
          escape('Prüfung'),
          escape(formatDate(e.naechste_pruefung_am)),
          escape(e.pruefung_tage_bis_faelligkeit),
        ].join(','));
      }

      const today = new Date();
      const dateStr = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
      const csv = '\uFEFF' + header + '\n' + rows.join('\n');

      res.setHeader('Content-Type', 'text/csv; charset=utf-8');
      res.setHeader('Content-Disposition', `attachment; filename="pruefungen_${dateStr}.csv"`);
      res.status(200).send(csv);
    } catch (error) {
      logger.error('exportAlerts error', { error });
      res.status(500).json({ success: false, message: 'Prüfungsexport konnte nicht erstellt werden' });
    }
  }

  async getVehicle(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const vehicle = await vehicleService.getVehicleById(id);
      if (!vehicle) {
        res.status(404).json({ success: false, message: 'Fahrzeug nicht gefunden' });
        return;
      }
      res.status(200).json({ success: true, data: vehicle });
    } catch (error) {
      logger.error('getVehicle error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Fahrzeug konnte nicht geladen werden' });
    }
  }

  async createVehicle(req: Request, res: Response): Promise<void> {
    try {
      const parsed = CreateFahrzeugSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      const vehicle = await vehicleService.createVehicle(parsed.data, getUserId(req));
      res.status(201).json({ success: true, data: vehicle });
    } catch (error) {
      logger.error('createVehicle error', { error });
      res.status(500).json({ success: false, message: 'Fahrzeug konnte nicht erstellt werden' });
    }
  }

  async updateVehicle(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const parsed = UpdateFahrzeugSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      if (Object.keys(parsed.data).length === 0) {
        res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
        return;
      }
      const vehicle = await vehicleService.updateVehicle(id, parsed.data, getUserId(req));
      res.status(200).json({ success: true, data: vehicle });
    } catch (error: any) {
      if (error?.message === 'Vehicle not found') {
        res.status(404).json({ success: false, message: 'Fahrzeug nicht gefunden' });
        return;
      }
      logger.error('updateVehicle error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Fahrzeug konnte nicht aktualisiert werden' });
    }
  }

  async updateVehicleStatus(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const parsed = UpdateStatusSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      const io = req.app.get('io') ?? undefined;
      const result = await vehicleService.updateVehicleStatus(
        id,
        parsed.data.status,
        parsed.data.bemerkung,
        getUserId(req),
        io,
        parsed.data.ausserDienstVon ? new Date(parsed.data.ausserDienstVon) : null,
        parsed.data.ausserDienstBis ? new Date(parsed.data.ausserDienstBis) : null,
      );

      // Fire-and-forget: notify scheduled messages when vehicle goes out of service
      if (parsed.data.status !== FahrzeugStatus.Einsatzbereit) {
        scheduledMessagesService.sendVehicleEvent(id).catch(err => {
          logger.error('Failed to send vehicle event notification', {
            vehicleId: id,
            error: err instanceof Error ? err.message : String(err),
          });
        });
      }

      res.status(200).json({ success: true, data: result });
    } catch (error: any) {
      if (error?.message === 'Vehicle not found') {
        res.status(404).json({ success: false, message: 'Fahrzeug nicht gefunden' });
        return;
      }
      logger.error('updateVehicleStatus error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Status konnte nicht aktualisiert werden' });
    }
  }

  async addWartung(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const parsed = CreateWartungslogSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      const entry = await vehicleService.addWartungslog(id, parsed.data, getUserId(req));
      res.status(201).json({ success: true, data: entry });
    } catch (error: any) {
      if (error?.message === 'Vehicle not found') {
        res.status(404).json({ success: false, message: 'Fahrzeug nicht gefunden' });
        return;
      }
      logger.error('addWartung error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Wartungseintrag konnte nicht gespeichert werden' });
    }
  }

  async deleteVehicle(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      await vehicleService.deleteVehicle(id, getUserId(req));
      res.status(204).send();
    } catch (error: any) {
      if (error?.message === 'Vehicle not found') {
        res.status(404).json({ success: false, message: 'Fahrzeug nicht gefunden' });
        return;
      }
      logger.error('deleteVehicle error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Fahrzeug konnte nicht gelöscht werden' });
    }
  }

  async getWartung(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const entries = await vehicleService.getWartungslogForVehicle(id);
      res.status(200).json({ success: true, data: entries });
    } catch (error) {
      logger.error('getWartung error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Wartungslog konnte nicht geladen werden' });
    }
  }

  async getStatusHistory(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      const history = await vehicleService.getStatusHistory(id);
      res.status(200).json({ success: true, data: history });
    } catch (error) {
      logger.error('getStatusHistory error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Status-Historie konnte nicht geladen werden' });
    }
  }

  async updateWartung(req: Request, res: Response): Promise<void> {
    try {
      const { id, wartungId } = req.params as Record<string, string>;
      if (!isValidUUID(id)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const parsed = UpdateWartungslogSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      const entry = await vehicleService.updateWartungslog(wartungId, id, parsed.data, getUserId(req));
      res.status(200).json({ success: true, data: entry });
    } catch (error: any) {
      if (error?.message === 'Wartungseintrag nicht gefunden') {
        res.status(404).json({ success: false, message: 'Wartungseintrag nicht gefunden' });
        return;
      }
      logger.error('updateWartung error', { error, id: req.params.id, wartungId: req.params.wartungId });
      res.status(500).json({ success: false, message: 'Wartungseintrag konnte nicht aktualisiert werden' });
    }
  }

  async uploadWartungFile(req: Request, res: Response): Promise<void> {
    const { wartungId } = req.params as Record<string, string>;
    const id = parseInt(wartungId, 10);
    if (isNaN(id)) {
      res.status(400).json({ success: false, message: 'Ungültige Wartungs-ID' });
      return;
    }
    const file = (req as any).file;
    if (!file) {
      res.status(400).json({ success: false, message: 'Keine Datei hochgeladen' });
      return;
    }
    try {
      const result = await vehicleService.updateWartungslogFile(id, file.path);
      res.status(200).json({ success: true, data: result });
    } catch (error) {
      logger.error('uploadWartungFile error', { error, wartungId });
      res.status(500).json({ success: false, message: 'Datei konnte nicht hochgeladen werden' });
    }
  }
}

export default new VehicleController();
