import { randomUUID } from 'crypto';
import db from '../../db.js';
import { badRequest, UUID } from '../../lib/http.js';
import { mapMachine } from '../../lib/mappers.js';

const ALLOWED_LIST_STATUS = new Set([
  '',
  'PRUEFEN',
  'VERSCHROTTET',
  'SN_GEAENDERT',
  'IN_BEARBEITUNG',
  'UPDATE_RAUS',
]);

function normalizeListStatus(v) {
  if (v == null || v === '') return '';
  const s = String(v).trim();
  return ALLOWED_LIST_STATUS.has(s) ? s : null;
}

/** @returns {{ ok: true, json: string | null } | { ok: false, message: string }} */
function parseExtrasField(extras) {
  if (extras === null || extras === '') {
    return { ok: true, json: null };
  }
  if (typeof extras === 'object' && extras !== null) {
    try {
      return { ok: true, json: JSON.stringify(extras) };
    } catch {
      return { ok: false, message: 'extras ist kein gültiges JSON-Objekt.' };
    }
  }
  if (typeof extras === 'string') {
    try {
      JSON.parse(extras);
      return { ok: true, json: extras };
    } catch {
      return { ok: false, message: 'extras ist kein gültiger JSON-String.' };
    }
  }
  return { ok: false, message: 'extras hat ein ungültiges Format.' };
}

export function registerMachineRoutes(api) {
  api.get('/machines', (_req, res) => {
    const rows = db
      .prepare('SELECT * FROM machines ORDER BY seriennummer ASC')
      .all();
    res.json(rows.map(mapMachine));
  });

  api.post('/machines', (req, res) => {
    const b = req.body || {};
    const { name, typ, seriennummer, standort, listStatus } = b;
    if (!name || !typ || !seriennummer || !standort) {
      return badRequest(res, 'Pflichtfelder fehlen.');
    }
    const ls = normalizeListStatus(listStatus);
    if (ls === null) return badRequest(res, 'Ungültiger Listen-Status.');
    let extrasJson = null;
    if (Object.prototype.hasOwnProperty.call(b, 'extras')) {
      const p = parseExtrasField(b.extras);
      if (!p.ok) return badRequest(res, p.message);
      extrasJson = p.json;
    }
    const id = randomUUID();
    const row = db
      .prepare(
        `INSERT INTO machines (id, name, typ, seriennummer, standort, list_status, extras, updated_at)
     VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now')) RETURNING *`,
      )
      .get(id, name, typ, seriennummer, standort, ls, extrasJson);
    res.status(201).json(mapMachine(row));
  });

  api.get('/machines/:id', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const row = db.prepare('SELECT * FROM machines WHERE id = ?').get(id);
    if (!row) return res.status(404).json({ message: 'Nicht gefunden' });
    res.json(mapMachine(row));
  });

  api.put('/machines/:id', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const cur = db.prepare('SELECT * FROM machines WHERE id = ?').get(id);
    if (!cur) return res.status(404).json({ message: 'Nicht gefunden' });
    const b = req.body || {};
    const next = {
      name: b.name ?? cur.name,
      typ: b.typ ?? cur.typ,
      seriennummer: b.seriennummer ?? cur.seriennummer,
      standort: b.standort ?? cur.standort,
    };
    let listStatusVal = cur.list_status ?? '';
    if (Object.prototype.hasOwnProperty.call(b, 'listStatus')) {
      const ls = normalizeListStatus(b.listStatus);
      if (ls === null) return badRequest(res, 'Ungültiger Listen-Status.');
      listStatusVal = ls;
    }
    let extrasJson = cur.extras;
    if (Object.prototype.hasOwnProperty.call(b, 'extras')) {
      const p = parseExtrasField(b.extras);
      if (!p.ok) return badRequest(res, p.message);
      extrasJson = p.json;
    }
    const row = db
      .prepare(
        `UPDATE machines SET name = ?, typ = ?, seriennummer = ?, standort = ?, list_status = ?, extras = ?, updated_at = datetime('now')
       WHERE id = ? RETURNING *`,
      )
      .get(
        next.name,
        next.typ,
        next.seriennummer,
        next.standort,
        listStatusVal,
        extrasJson,
        id,
      );
    res.json(mapMachine(row));
  });

  api.delete('/machines/:id', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const cur = db.prepare('SELECT * FROM machines WHERE id = ?').get(id);
    if (!cur) return res.status(404).json({ message: 'Nicht gefunden' });
    const tc = db
      .prepare('SELECT COUNT(*) AS c FROM tickets WHERE machine_id = ?')
      .get(id);
    if (tc.c > 0) {
      return res.status(409).json({
        message:
          'Maschine kann nicht gelöscht werden: Es existieren noch zugeordnete Tickets.',
      });
    }
    const row = db
      .prepare('DELETE FROM machines WHERE id = ? RETURNING *')
      .get(id);
    res.json(mapMachine(row));
  });
}
