import pool from '../config/database';
import logger from '../utils/logger';
import {
  Ausruestung,
  AusruestungKategorie,
  AusruestungListItem,
  AusruestungDetail,
  AusruestungWartungslog,
  CreateAusruestungData,
  UpdateAusruestungData,
  CreateAusruestungWartungslogData,
  UpdateAusruestungWartungslogData,
  AusruestungStatus,
  EquipmentStats,
  VehicleEquipmentWarning,
} from '../models/equipment.model';

class EquipmentService {
  // =========================================================================
  // EQUIPMENT OVERVIEW
  // =========================================================================

  async getAllEquipment(): Promise<AusruestungListItem[]> {
    try {
      const result = await pool.query(`
        SELECT *
        FROM ausruestung_mit_pruefstatus
        ORDER BY kategorie_kurzname, bezeichnung
      `);

      return result.rows.map((row) => ({
        ...row,
        pruefung_tage_bis_faelligkeit: row.pruefung_tage_bis_faelligkeit != null
          ? parseInt(row.pruefung_tage_bis_faelligkeit, 10) : null,
      })) as AusruestungListItem[];
    } catch (error) {
      logger.error('EquipmentService.getAllEquipment failed', { error });
      throw new Error('Failed to fetch equipment');
    }
  }

  // =========================================================================
  // EQUIPMENT DETAIL
  // =========================================================================

  async getEquipmentById(id: string): Promise<AusruestungDetail | null> {
    try {
      const equipmentResult = await pool.query(
        `SELECT * FROM ausruestung_mit_pruefstatus WHERE id = $1`,
        [id]
      );

      if (equipmentResult.rows.length === 0) return null;

      const row = equipmentResult.rows[0];

      const wartungslogResult = await pool.query(
        `SELECT * FROM ausruestung_wartungslog
         WHERE ausruestung_id = $1
         ORDER BY datum DESC, created_at DESC`,
        [id]
      );

      const equipment: AusruestungDetail = {
        ...row,
        pruefung_tage_bis_faelligkeit: row.pruefung_tage_bis_faelligkeit != null
          ? parseInt(row.pruefung_tage_bis_faelligkeit, 10) : null,
        wartungslog: wartungslogResult.rows.map(r => ({
          ...r,
          kosten: r.kosten != null ? Number(r.kosten) : null,
        })) as AusruestungWartungslog[],
      };

      return equipment;
    } catch (error) {
      logger.error('EquipmentService.getEquipmentById failed', { error, id });
      throw new Error('Failed to fetch equipment');
    }
  }

  // =========================================================================
  // EQUIPMENT BY VEHICLE
  // =========================================================================

  async getEquipmentByVehicle(fahrzeugId: string): Promise<AusruestungListItem[]> {
    try {
      const result = await pool.query(
        `SELECT *
         FROM ausruestung_mit_pruefstatus
         WHERE fahrzeug_id = $1
         ORDER BY kategorie_kurzname, bezeichnung`,
        [fahrzeugId]
      );

      return result.rows.map((row) => ({
        ...row,
        pruefung_tage_bis_faelligkeit: row.pruefung_tage_bis_faelligkeit != null
          ? parseInt(row.pruefung_tage_bis_faelligkeit, 10) : null,
      })) as AusruestungListItem[];
    } catch (error) {
      logger.error('EquipmentService.getEquipmentByVehicle failed', { error, fahrzeugId });
      throw new Error('Failed to fetch equipment for vehicle');
    }
  }

  // =========================================================================
  // CATEGORIES
  // =========================================================================

  async getCategories(): Promise<AusruestungKategorie[]> {
    try {
      const result = await pool.query(
        `SELECT * FROM ausruestung_kategorien ORDER BY sortierung`
      );
      return result.rows as AusruestungKategorie[];
    } catch (error) {
      logger.error('EquipmentService.getCategories failed', { error });
      throw new Error('Failed to fetch equipment categories');
    }
  }

  async getCategoryById(id: string): Promise<AusruestungKategorie | null> {
    try {
      const result = await pool.query(
        `SELECT * FROM ausruestung_kategorien WHERE id = $1`,
        [id]
      );
      return result.rows.length > 0 ? (result.rows[0] as AusruestungKategorie) : null;
    } catch (error) {
      logger.error('EquipmentService.getCategoryById failed', { error, id });
      throw new Error('Failed to fetch equipment category');
    }
  }

  async createCategory(data: { name: string; kurzname: string; sortierung?: number; motorisiert?: boolean }): Promise<AusruestungKategorie> {
    try {
      const result = await pool.query(
        `INSERT INTO ausruestung_kategorien (id, name, kurzname, sortierung, motorisiert)
         VALUES (uuid_generate_v4(), $1, $2, COALESCE($3, (SELECT COALESCE(MAX(sortierung),0)+1 FROM ausruestung_kategorien)), COALESCE($4, false))
         RETURNING *`,
        [data.name, data.kurzname, data.sortierung ?? null, data.motorisiert ?? null]
      );
      logger.info('Equipment category created', { id: result.rows[0].id, name: data.name });
      return result.rows[0] as AusruestungKategorie;
    } catch (error) {
      logger.error('EquipmentService.createCategory failed', { error });
      throw new Error('Failed to create equipment category');
    }
  }

  async updateCategory(id: string, data: { name?: string; kurzname?: string; sortierung?: number; motorisiert?: boolean }): Promise<AusruestungKategorie | null> {
    try {
      const fields: string[] = [];
      const values: unknown[] = [];
      let p = 1;

      if (data.name !== undefined)       { fields.push(`name = $${p++}`);       values.push(data.name); }
      if (data.kurzname !== undefined)    { fields.push(`kurzname = $${p++}`);    values.push(data.kurzname); }
      if (data.sortierung !== undefined)  { fields.push(`sortierung = $${p++}`);  values.push(data.sortierung); }
      if (data.motorisiert !== undefined) { fields.push(`motorisiert = $${p++}`); values.push(data.motorisiert); }

      if (fields.length === 0) throw new Error('No fields to update');

      values.push(id);
      const result = await pool.query(
        `UPDATE ausruestung_kategorien SET ${fields.join(', ')} WHERE id = $${p} RETURNING *`,
        values
      );
      if (result.rows.length === 0) return null;
      logger.info('Equipment category updated', { id });
      return result.rows[0] as AusruestungKategorie;
    } catch (error) {
      logger.error('EquipmentService.updateCategory failed', { error, id });
      throw error;
    }
  }

  async deleteCategory(id: string): Promise<{ deleted: boolean; error?: string }> {
    try {
      // Check if any equipment items reference this category
      const usage = await pool.query(
        `SELECT COUNT(*) AS cnt FROM ausruestung WHERE kategorie_id = $1 AND deleted_at IS NULL`,
        [id]
      );
      const count = parseInt(usage.rows[0].cnt, 10);
      if (count > 0) {
        return { deleted: false, error: `Kategorie wird von ${count} Ausrüstungsgegenständen verwendet und kann nicht gelöscht werden.` };
      }
      const result = await pool.query(
        `DELETE FROM ausruestung_kategorien WHERE id = $1 RETURNING id`,
        [id]
      );
      if (result.rows.length === 0) {
        return { deleted: false, error: 'Kategorie nicht gefunden' };
      }
      logger.info('Equipment category deleted', { id });
      return { deleted: true };
    } catch (error) {
      logger.error('EquipmentService.deleteCategory failed', { error, id });
      throw new Error('Failed to delete equipment category');
    }
  }

  // =========================================================================
  // CRUD
  // =========================================================================

  async createEquipment(data: CreateAusruestungData, createdBy: string): Promise<Ausruestung> {
    try {
      const result = await pool.query(
        `INSERT INTO ausruestung (
           id, bezeichnung, kategorie_id, seriennummer, inventarnummer,
           hersteller, baujahr, status, status_bemerkung, ist_wichtig,
           fahrzeug_id, standort, pruef_intervall_monate,
           letzte_pruefung_am, naechste_pruefung_am, bemerkung
         ) VALUES (uuid_generate_v4(),$1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
         RETURNING *`,
        [
          data.bezeichnung,
          data.kategorie_id,
          data.seriennummer            ?? null,
          data.inventarnummer          ?? null,
          data.hersteller              ?? null,
          data.baujahr                 ?? null,
          data.status                  ?? AusruestungStatus.Einsatzbereit,
          data.status_bemerkung        ?? null,
          data.ist_wichtig             ?? false,
          data.fahrzeug_id             ?? null,
          data.standort                ?? 'Lager',
          data.pruef_intervall_monate  ?? null,
          data.letzte_pruefung_am      ?? null,
          data.naechste_pruefung_am    ?? null,
          data.bemerkung               ?? null,
        ]
      );

      const equipment = result.rows[0] as Ausruestung;
      logger.info('Equipment created', { id: equipment.id, by: createdBy });
      return equipment;
    } catch (error) {
      logger.error('EquipmentService.createEquipment failed', { error, createdBy });
      throw new Error('Failed to create equipment');
    }
  }

  async updateEquipment(id: string, data: UpdateAusruestungData, updatedBy: string): Promise<Ausruestung | null> {
    try {
      const fields: string[] = [];
      const values: unknown[] = [];
      let p = 1;

      const addField = (col: string, value: unknown) => {
        fields.push(`${col} = $${p++}`);
        values.push(value);
      };

      if (data.bezeichnung            !== undefined) addField('bezeichnung',            data.bezeichnung);
      if (data.kategorie_id           !== undefined) addField('kategorie_id',           data.kategorie_id);
      if (data.seriennummer           !== undefined) addField('seriennummer',           data.seriennummer);
      if (data.inventarnummer         !== undefined) addField('inventarnummer',         data.inventarnummer);
      if (data.hersteller             !== undefined) addField('hersteller',             data.hersteller);
      if (data.baujahr                !== undefined) addField('baujahr',                data.baujahr);
      if (data.status                 !== undefined) addField('status',                 data.status);
      if (data.status_bemerkung       !== undefined) addField('status_bemerkung',       data.status_bemerkung);
      if (data.ist_wichtig            !== undefined) addField('ist_wichtig',            data.ist_wichtig);
      if (data.fahrzeug_id            !== undefined) addField('fahrzeug_id',            data.fahrzeug_id);
      if (data.standort               !== undefined) addField('standort',               data.standort);
      if (data.pruef_intervall_monate !== undefined) addField('pruef_intervall_monate', data.pruef_intervall_monate);
      if (data.letzte_pruefung_am     !== undefined) addField('letzte_pruefung_am',     data.letzte_pruefung_am);
      if (data.naechste_pruefung_am   !== undefined) addField('naechste_pruefung_am',   data.naechste_pruefung_am);
      if (data.bemerkung              !== undefined) addField('bemerkung',              data.bemerkung);

      if (fields.length === 0) {
        throw new Error('No fields to update');
      }

      values.push(id);
      const result = await pool.query(
        `UPDATE ausruestung SET ${fields.join(', ')} WHERE id = $${p} AND deleted_at IS NULL RETURNING *`,
        values
      );

      if (result.rows.length === 0) {
        return null;
      }

      const equipment = result.rows[0] as Ausruestung;
      logger.info('Equipment updated', { id, by: updatedBy });
      return equipment;
    } catch (error) {
      logger.error('EquipmentService.updateEquipment failed', { error, id, updatedBy });
      throw error;
    }
  }

  async deleteEquipment(id: string, deletedBy: string): Promise<boolean> {
    try {
      const result = await pool.query(
        `UPDATE ausruestung
         SET deleted_at = NOW()
         WHERE id = $1 AND deleted_at IS NULL
         RETURNING id`,
        [id]
      );

      if (result.rows.length === 0) {
        return false;
      }

      logger.info('Equipment soft-deleted', { id, by: deletedBy });
      return true;
    } catch (error) {
      logger.error('EquipmentService.deleteEquipment failed', { error, id });
      throw error;
    }
  }

  // =========================================================================
  // STATUS MANAGEMENT
  // =========================================================================

  async updateStatus(
    id: string,
    status: AusruestungStatus,
    bemerkung: string,
    updatedBy: string
  ): Promise<void> {
    try {
      // Get old status for history
      const oldResult = await pool.query(
        `SELECT status FROM ausruestung WHERE id = $1 AND deleted_at IS NULL`,
        [id]
      );
      const oldStatus = oldResult.rows[0]?.status;

      const result = await pool.query(
        `UPDATE ausruestung
         SET status = $1, status_bemerkung = $2, updated_at = NOW()
         WHERE id = $3 AND deleted_at IS NULL
         RETURNING id`,
        [status, bemerkung || null, id]
      );

      if (result.rows.length === 0) {
        throw new Error('Equipment not found');
      }

      // Record status change history
      if (oldStatus && oldStatus !== status) {
        await pool.query(
          `INSERT INTO ausruestung_status_historie (ausruestung_id, alter_status, neuer_status, bemerkung, geaendert_von)
           VALUES ($1, $2, $3, $4, $5)`,
          [id, oldStatus, status, bemerkung || null, updatedBy]
        );
      }

      logger.info('Equipment status updated', { id, status, by: updatedBy });
    } catch (error) {
      logger.error('EquipmentService.updateStatus failed', { error, id });
      throw error;
    }
  }

  // =========================================================================
  // MAINTENANCE LOG
  // =========================================================================

  async addWartungslog(
    equipmentId: string,
    data: CreateAusruestungWartungslogData,
    createdBy: string
  ): Promise<AusruestungWartungslog> {
    try {
      const check = await pool.query(
        `SELECT 1 FROM ausruestung WHERE id = $1 AND deleted_at IS NULL`,
        [equipmentId]
      );
      if (check.rows.length === 0) {
        throw new Error('Equipment not found');
      }

      const result = await pool.query(
        `INSERT INTO ausruestung_wartungslog (
           ausruestung_id, datum, art, beschreibung,
           ergebnis, kosten, pruefende_stelle, dokument_url, erfasst_von
         ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9)
         RETURNING *`,
        [
          equipmentId,
          data.datum,
          data.art,
          data.beschreibung,
          data.ergebnis          ?? null,
          data.kosten            ?? null,
          data.pruefende_stelle  ?? null,
          data.dokument_url      ?? null,
          createdBy,
        ]
      );

      const entry = result.rows[0] as AusruestungWartungslog;
      logger.info('Equipment wartungslog entry added', { entryId: entry.id, equipmentId, by: createdBy });

      // Auto-update next inspection date on the equipment when result is 'bestanden'
      if (data.ergebnis === 'bestanden' && data.naechste_pruefung_am) {
        await pool.query(
          `UPDATE ausruestung SET naechste_pruefung_am = $1, letzte_pruefung_am = $2 WHERE id = $3`,
          [data.naechste_pruefung_am, data.datum, equipmentId]
        );
      }

      return entry;
    } catch (error) {
      logger.error('EquipmentService.addWartungslog failed', { error, equipmentId });
      throw error;
    }
  }

  // =========================================================================
  // DASHBOARD KPI
  // =========================================================================

  async getEquipmentStats(): Promise<EquipmentStats> {
    try {
      const result = await pool.query(`
        SELECT
          COUNT(*)                                                AS total,
          COUNT(*) FILTER (WHERE status = 'einsatzbereit')       AS einsatzbereit,
          COUNT(*) FILTER (WHERE status = 'beschaedigt')         AS beschaedigt,
          COUNT(*) FILTER (WHERE status = 'in_wartung')          AS in_wartung,
          COUNT(*) FILTER (WHERE status = 'ausser_dienst')       AS ausser_dienst,
          COUNT(*) FILTER (
            WHERE naechste_pruefung_am IS NOT NULL
              AND naechste_pruefung_am::date - CURRENT_DATE BETWEEN 0 AND 30
          )                                                      AS inspections_due,
          COUNT(*) FILTER (
            WHERE naechste_pruefung_am IS NOT NULL
              AND naechste_pruefung_am::date < CURRENT_DATE
          )                                                      AS inspections_overdue,
          COUNT(*) FILTER (
            WHERE ist_wichtig = TRUE
              AND status != 'einsatzbereit'
          )                                                      AS wichtig_nicht_bereit
        FROM ausruestung
        WHERE deleted_at IS NULL
      `);

      const row = result.rows[0] ?? {};

      return {
        total:              parseInt(row.total ?? '0', 10),
        einsatzbereit:      parseInt(row.einsatzbereit ?? '0', 10),
        beschaedigt:        parseInt(row.beschaedigt ?? '0', 10),
        inWartung:          parseInt(row.in_wartung ?? '0', 10),
        ausserDienst:       parseInt(row.ausser_dienst ?? '0', 10),
        inspectionsDue:     parseInt(row.inspections_due ?? '0', 10),
        inspectionsOverdue: parseInt(row.inspections_overdue ?? '0', 10),
        wichtigNichtBereit: parseInt(row.wichtig_nicht_bereit ?? '0', 10),
      };
    } catch (error) {
      logger.error('EquipmentService.getEquipmentStats failed', { error });
      throw new Error('Failed to fetch equipment stats');
    }
  }

  // =========================================================================
  // VEHICLE WARNINGS
  // =========================================================================

  async getVehicleWarnings(): Promise<VehicleEquipmentWarning[]> {
    try {
      const result = await pool.query(`
        SELECT
          a.fahrzeug_id,
          a.id AS ausruestung_id,
          a.bezeichnung,
          a.status,
          k.name AS kategorie_name
        FROM ausruestung a
        JOIN ausruestung_kategorien k ON k.id = a.kategorie_id
        WHERE a.ist_wichtig = TRUE
          AND a.fahrzeug_id IS NOT NULL
          AND a.status != 'einsatzbereit'
          AND a.deleted_at IS NULL
        ORDER BY a.fahrzeug_id
      `);

      return result.rows as VehicleEquipmentWarning[];
    } catch (error) {
      logger.error('EquipmentService.getVehicleWarnings failed', { error });
      throw new Error('Failed to fetch vehicle equipment warnings');
    }
  }

  // =========================================================================
  // UPCOMING INSPECTIONS
  // =========================================================================

  async getUpcomingInspections(daysAhead: number = 30): Promise<AusruestungListItem[]> {
    try {
      const result = await pool.query(
        `SELECT *
         FROM ausruestung_mit_pruefstatus
         WHERE pruefung_tage_bis_faelligkeit IS NOT NULL
           AND pruefung_tage_bis_faelligkeit <= $1
         ORDER BY pruefung_tage_bis_faelligkeit ASC`,
        [daysAhead]
      );

      return result.rows.map((row) => ({
        ...row,
        pruefung_tage_bis_faelligkeit: row.pruefung_tage_bis_faelligkeit != null
          ? parseInt(row.pruefung_tage_bis_faelligkeit, 10) : null,
      })) as AusruestungListItem[];
    } catch (error) {
      logger.error('EquipmentService.getUpcomingInspections failed', { error, daysAhead });
      throw new Error('Failed to fetch upcoming inspections');
    }
  }

  // =========================================================================
  // STATUS HISTORY
  // =========================================================================

  async getStatusHistory(equipmentId: string) {
    try {
      const result = await pool.query(
        `SELECT h.*, COALESCE(u.name, u.preferred_username, u.email) AS geaendert_von_name
         FROM ausruestung_status_historie h
         LEFT JOIN users u ON u.id = h.geaendert_von
         WHERE h.ausruestung_id = $1
         ORDER BY h.erstellt_am DESC
         LIMIT 50`,
        [equipmentId]
      );
      return result.rows;
    } catch (error) {
      logger.error('EquipmentService.getStatusHistory failed', { error, equipmentId });
      throw new Error('Status-Historie konnte nicht geladen werden');
    }
  }

  // =========================================================================
  // WARTUNGSLOG UPDATE
  // =========================================================================

  async updateWartungslog(
    equipmentId: string,
    wartungId: number,
    data: UpdateAusruestungWartungslogData,
    updatedBy: string
  ): Promise<AusruestungWartungslog> {
    try {
      // Verify the wartung entry belongs to this equipment
      const check = await pool.query(
        `SELECT id FROM ausruestung_wartungslog WHERE id = $1 AND ausruestung_id = $2`,
        [wartungId, equipmentId]
      );
      if (check.rows.length === 0) {
        throw new Error('Wartungseintrag nicht gefunden');
      }

      const fields: string[] = [];
      const values: unknown[] = [];
      let p = 1;

      const addField = (col: string, value: unknown) => {
        fields.push(`${col} = $${p++}`);
        values.push(value);
      };

      if (data.datum             !== undefined) addField('datum',             data.datum);
      if (data.art               !== undefined) addField('art',               data.art);
      if (data.beschreibung      !== undefined) addField('beschreibung',      data.beschreibung);
      if (data.ergebnis          !== undefined) addField('ergebnis',          data.ergebnis);
      if (data.kosten            !== undefined) addField('kosten',            data.kosten);
      if (data.pruefende_stelle  !== undefined) addField('pruefende_stelle',  data.pruefende_stelle);

      if (fields.length === 0) {
        throw new Error('No fields to update');
      }

      values.push(wartungId);
      const result = await pool.query(
        `UPDATE ausruestung_wartungslog SET ${fields.join(', ')} WHERE id = $${p} RETURNING *`,
        values
      );

      const entry = result.rows[0] as AusruestungWartungslog;

      // Auto-update next inspection date on the equipment when result is 'bestanden'
      if (data.ergebnis === 'bestanden' && data.naechste_pruefung_am) {
        await pool.query(
          `UPDATE ausruestung SET naechste_pruefung_am = $1, letzte_pruefung_am = $2 WHERE id = $3`,
          [data.naechste_pruefung_am, data.datum ?? entry.datum, equipmentId]
        );
      }

      logger.info('Equipment wartungslog entry updated', { wartungId, equipmentId, by: updatedBy });
      return entry;
    } catch (error) {
      logger.error('EquipmentService.updateWartungslog failed', { error, wartungId, equipmentId });
      throw error;
    }
  }

  // =========================================================================
  // WARTUNGSLOG FILE UPLOAD
  // =========================================================================

  async updateWartungslogFile(wartungId: number, filePath: string) {
    try {
      const result = await pool.query(
        `UPDATE ausruestung_wartungslog SET dokument_url = $1 WHERE id = $2 RETURNING *`,
        [filePath, wartungId]
      );
      if (result.rows.length === 0) {
        throw new Error('Wartungseintrag nicht gefunden');
      }
      return result.rows[0];
    } catch (error) {
      logger.error('EquipmentService.updateWartungslogFile failed', { error, wartungId });
      throw error;
    }
  }
}

export default new EquipmentService();
