import { Request, Response } from 'express';
import { z } from 'zod';
import equipmentService from '../services/equipment.service';
import { AusruestungStatus } from '../models/equipment.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 AusruestungStatusEnum = z.enum([
  AusruestungStatus.Einsatzbereit,
  AusruestungStatus.Beschaedigt,
  AusruestungStatus.InWartung,
  AusruestungStatus.AusserDienst,
]);

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 uuidString = z.string().regex(
  /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
  'Ungültige UUID'
);

const CreateAusruestungSchema = z.object({
  bezeichnung:             z.string().min(1).max(200),
  kategorie_id:            uuidString,
  seriennummer:            z.string().max(100).optional(),
  inventarnummer:          z.string().max(50).optional(),
  hersteller:              z.string().max(150).optional(),
  baujahr:                 z.number().int().min(1950).max(2100).optional(),
  status:                  AusruestungStatusEnum.optional(),
  status_bemerkung:        z.string().max(2000).optional(),
  ist_wichtig:             z.boolean().optional(),
  fahrzeug_id:             uuidString.optional(),
  standort:                z.string().min(1).max(150).optional(),
  pruef_intervall_monate:  z.number().int().min(1).optional(),
  letzte_pruefung_am:      isoDate.optional(),
  naechste_pruefung_am:    isoDate.optional(),
  bemerkung:               z.string().max(2000).optional(),
});

const UpdateAusruestungSchema = z.object({
  bezeichnung:             z.string().min(1).max(200).optional(),
  kategorie_id:            uuidString.optional(),
  seriennummer:            z.string().max(100).nullable().optional(),
  inventarnummer:          z.string().max(50).nullable().optional(),
  hersteller:              z.string().max(150).nullable().optional(),
  baujahr:                 z.number().int().min(1950).max(2100).nullable().optional(),
  status:                  AusruestungStatusEnum.optional(),
  status_bemerkung:        z.string().max(2000).nullable().optional(),
  ist_wichtig:             z.boolean().optional(),
  fahrzeug_id:             uuidString.nullable().optional(),
  standort:                z.string().min(1).max(150).optional(),
  pruef_intervall_monate:  z.number().int().min(1).nullable().optional(),
  letzte_pruefung_am:      isoDate.nullable().optional(),
  naechste_pruefung_am:    isoDate.nullable().optional(),
  bemerkung:               z.string().max(2000).nullable().optional(),
});

const UpdateStatusSchema = z.object({
  status:    AusruestungStatusEnum,
  bemerkung: z.string().max(2000).optional().default(''),
});

const CreateWartungslogSchema = z.object({
  datum:              isoDate,
  art:                z.enum(['Prüfung', 'Reparatur', 'Sonstiges']),
  beschreibung:       z.string().min(1).max(2000),
  ergebnis:           z.enum(['bestanden', 'bestanden_mit_maengeln', 'nicht_bestanden']).optional(),
  kosten:             z.number().min(0).optional(),
  pruefende_stelle:   z.string().max(150).optional(),
  dokument_url:       z.string().url().max(500).refine(
    (url) => /^https?:\/\//i.test(url),
    'Nur http/https URLs erlaubt'
  ).optional(),
  naechste_pruefung_am: isoDate.optional(),
});

const UpdateWartungslogSchema = z.object({
  datum:              isoDate.optional(),
  art:                z.enum(['Prüfung', 'Reparatur', 'Sonstiges']).optional(),
  beschreibung:       z.string().min(1).max(2000).optional(),
  ergebnis:           z.enum(['bestanden', 'bestanden_mit_maengeln', 'nicht_bestanden']).nullable().optional(),
  kosten:             z.number().min(0).nullable().optional(),
  pruefende_stelle:   z.string().max(150).nullable().optional(),
  naechste_pruefung_am: isoDate.nullable().optional(),
});

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

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

function getUserGroups(req: Request): string[] {
  return req.user?.groups ?? [];
}

/**
 * Returns true if the user is authorised to write to equipment in the given
 * category.  Admin can write to any category.  Fahrmeister can only write to
 * motorised categories.  Zeugmeister can only write to non-motorised categories.
 */
async function checkCategoryPermission(kategorieId: string, groups: string[]): Promise<boolean> {
  if (groups.includes('dashboard_admin')) return true;

  const result = await equipmentService.getCategoryById(kategorieId);
  if (!result) return false; // unknown category → deny

  if (result.motorisiert) {
    return groups.includes('dashboard_fahrmeister');
  } else {
    return groups.includes('dashboard_zeugmeister');
  }
}

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

class EquipmentController {
  async listEquipment(_req: Request, res: Response): Promise<void> {
    try {
      const equipment = await equipmentService.getAllEquipment();
      res.status(200).json({ success: true, data: equipment });
    } catch (error) {
      logger.error('listEquipment error', { error });
      res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht geladen werden' });
    }
  }

  async getEquipment(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 Ausrüstungs-ID' });
        return;
      }
      const equipment = await equipmentService.getEquipmentById(id);
      if (!equipment) {
        res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
        return;
      }
      res.status(200).json({ success: true, data: equipment });
    } catch (error) {
      logger.error('getEquipment error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht geladen werden' });
    }
  }

  async getByVehicle(req: Request, res: Response): Promise<void> {
    try {
      const { fahrzeugId } = req.params as Record<string, string>;
      if (!isValidUUID(fahrzeugId)) {
        res.status(400).json({ success: false, message: 'Ungültige Fahrzeug-ID' });
        return;
      }
      const equipment = await equipmentService.getEquipmentByVehicle(fahrzeugId);
      res.status(200).json({ success: true, data: equipment });
    } catch (error) {
      logger.error('getByVehicle error', { error, fahrzeugId: req.params.fahrzeugId });
      res.status(500).json({ success: false, message: 'Ausrüstung für Fahrzeug konnte nicht geladen werden' });
    }
  }

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

  async getStats(_req: Request, res: Response): Promise<void> {
    try {
      const stats = await equipmentService.getEquipmentStats();
      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 equipmentService.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 getVehicleWarnings(_req: Request, res: Response): Promise<void> {
    try {
      const warnings = await equipmentService.getVehicleWarnings();
      res.status(200).json({ success: true, data: warnings });
    } catch (error) {
      logger.error('getVehicleWarnings error', { error });
      res.status(500).json({ success: false, message: 'Fahrzeug-Warnungen konnten nicht geladen werden' });
    }
  }

  async createEquipment(req: Request, res: Response): Promise<void> {
    try {
      const parsed = CreateAusruestungSchema.safeParse(req.body);
      if (!parsed.success) {
        res.status(400).json({
          success: false,
          message: 'Validierungsfehler',
          errors: parsed.error.flatten().fieldErrors,
        });
        return;
      }
      const groups = getUserGroups(req);
      const allowed = await checkCategoryPermission(parsed.data.kategorie_id, groups);
      if (!allowed) {
        res.status(403).json({ success: false, message: 'Keine Berechtigung für diese Kategorie' });
        return;
      }
      const equipment = await equipmentService.createEquipment(parsed.data, getUserId(req));
      res.status(201).json({ success: true, data: equipment });
    } catch (error) {
      logger.error('createEquipment error', { error });
      res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht erstellt werden' });
    }
  }

  async updateEquipment(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 Ausrüstungs-ID' });
        return;
      }
      const parsed = UpdateAusruestungSchema.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;
      }
      // Determine which category to check permissions against
      const groups = getUserGroups(req);
      if (!groups.includes('dashboard_admin')) {
        // Always fetch existing equipment to check old category permission
        const existing = await equipmentService.getEquipmentById(id);
        if (!existing) {
          res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
          return;
        }
        // Check permission against the OLD category (must be allowed to move FROM it)
        const allowedOld = await checkCategoryPermission(existing.kategorie_id, groups);
        if (!allowedOld) {
          res.status(403).json({ success: false, message: 'Keine Berechtigung für diese Kategorie' });
          return;
        }
        // If kategorie_id is being changed, also check permission against the NEW category
        if (parsed.data.kategorie_id && parsed.data.kategorie_id !== existing.kategorie_id) {
          const allowedNew = await checkCategoryPermission(parsed.data.kategorie_id, groups);
          if (!allowedNew) {
            res.status(403).json({ success: false, message: 'Keine Berechtigung für die Ziel-Kategorie' });
            return;
          }
        }
      }
      const equipment = await equipmentService.updateEquipment(id, parsed.data, getUserId(req));
      if (!equipment) {
        res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
        return;
      }
      res.status(200).json({ success: true, data: equipment });
    } catch (error: any) {
      if (error?.message === 'No fields to update') {
        res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
        return;
      }
      logger.error('updateEquipment error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht aktualisiert werden' });
    }
  }

  async updateStatus(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 Ausrüstungs-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 groups = getUserGroups(req);
      if (!groups.includes('dashboard_admin')) {
        const existing = await equipmentService.getEquipmentById(id);
        if (!existing) {
          res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
          return;
        }
        const allowed = await checkCategoryPermission(existing.kategorie_id, groups);
        if (!allowed) {
          res.status(403).json({ success: false, message: 'Keine Berechtigung für diese Kategorie' });
          return;
        }
      }
      await equipmentService.updateStatus(
        id, parsed.data.status, parsed.data.bemerkung, getUserId(req)
      );
      res.status(200).json({ success: true, message: 'Status aktualisiert' });
    } catch (error: any) {
      if (error?.message === 'Equipment not found') {
        res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
        return;
      }
      logger.error('updateStatus error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Status konnte nicht aktualisiert werden' });
    }
  }

  async deleteEquipment(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 Ausrüstungs-ID' });
        return;
      }
      const deleted = await equipmentService.deleteEquipment(id, getUserId(req));
      if (!deleted) {
        res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
        return;
      }
      res.status(200).json({ success: true, message: 'Ausrüstung gelöscht' });
    } catch (error) {
      logger.error('deleteEquipment error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Ausrüstung konnte nicht gelöscht 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 Ausrüstungs-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 groups = getUserGroups(req);
      if (!groups.includes('dashboard_admin')) {
        const existing = await equipmentService.getEquipmentById(id);
        if (!existing) {
          res.status(404).json({ success: false, message: 'Ausrüstung nicht gefunden' });
          return;
        }
        const allowed = await checkCategoryPermission(existing.kategorie_id, groups);
        if (!allowed) {
          res.status(403).json({ success: false, message: 'Keine Berechtigung für diese Kategorie' });
          return;
        }
      }
      const entry = await equipmentService.addWartungslog(id, parsed.data, getUserId(req));
      res.status(201).json({ success: true, data: entry });
    } catch (error: any) {
      if (error?.message === 'Equipment not found') {
        res.status(404).json({ success: false, message: 'Ausrüstung 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 getStatusHistory(req: Request, res: Response): Promise<void> {
    try {
      const { id } = req.params as Record<string, string>;
      const history = await equipmentService.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 Ausrüstungs-ID' });
        return;
      }
      const wId = parseInt(wartungId, 10);
      if (isNaN(wId)) {
        res.status(400).json({ success: false, message: 'Ungültige Wartungs-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;
      }
      if (Object.keys(parsed.data).length === 0) {
        res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
        return;
      }
      const entry = await equipmentService.updateWartungslog(id, wId, 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: error.message });
        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 createCategory(req: Request, res: Response): Promise<void> {
    try {
      const { name, kurzname, sortierung, motorisiert } = req.body;
      if (!name || typeof name !== 'string' || !name.trim()) {
        res.status(400).json({ success: false, message: 'Name ist erforderlich' });
        return;
      }
      if (!kurzname || typeof kurzname !== 'string' || !kurzname.trim()) {
        res.status(400).json({ success: false, message: 'Kurzname ist erforderlich' });
        return;
      }
      const category = await equipmentService.createCategory({
        name: name.trim(),
        kurzname: kurzname.trim(),
        sortierung: sortierung != null ? Number(sortierung) : undefined,
        motorisiert: motorisiert != null ? Boolean(motorisiert) : undefined,
      });
      res.status(201).json({ success: true, data: category });
    } catch (error) {
      logger.error('createCategory error', { error });
      res.status(500).json({ success: false, message: 'Kategorie konnte nicht erstellt werden' });
    }
  }

  async updateCategory(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 Kategorie-ID' });
        return;
      }
      const { name, kurzname, sortierung, motorisiert } = req.body;
      const data: Record<string, unknown> = {};
      if (name !== undefined) data.name = String(name).trim();
      if (kurzname !== undefined) data.kurzname = String(kurzname).trim();
      if (sortierung !== undefined) data.sortierung = Number(sortierung);
      if (motorisiert !== undefined) data.motorisiert = Boolean(motorisiert);

      if (Object.keys(data).length === 0) {
        res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
        return;
      }
      const category = await equipmentService.updateCategory(id, data as any);
      if (!category) {
        res.status(404).json({ success: false, message: 'Kategorie nicht gefunden' });
        return;
      }
      res.status(200).json({ success: true, data: category });
    } catch (error: any) {
      if (error?.message === 'No fields to update') {
        res.status(400).json({ success: false, message: 'Kein Feld zum Aktualisieren angegeben' });
        return;
      }
      logger.error('updateCategory error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Kategorie konnte nicht aktualisiert werden' });
    }
  }

  async deleteCategory(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 Kategorie-ID' });
        return;
      }
      const result = await equipmentService.deleteCategory(id);
      if (!result.deleted) {
        res.status(result.error === 'Kategorie nicht gefunden' ? 404 : 409).json({
          success: false,
          message: result.error,
        });
        return;
      }
      res.status(200).json({ success: true, message: 'Kategorie gelöscht' });
    } catch (error) {
      logger.error('deleteCategory error', { error, id: req.params.id });
      res.status(500).json({ success: false, message: 'Kategorie konnte nicht gelöscht 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 equipmentService.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 EquipmentController();
