import { randomUUID } from 'crypto';
import db from '../../db.js';
import { badRequest, UUID } from '../../lib/http.js';
import { mergeAttachmentEventsForApi } from '../../lib/ticket-events-merge.js';
import {
  mapTicket,
  ticketJoinSelect,
  ticketLastActivityExpr,
  ticketSlaDueExpr,
} from '../../lib/mappers.js';

function parseSlaDaysForCreate(v) {
  if (v === undefined || v === null || v === '') return null;
  const n = Number(v);
  if (!Number.isInteger(n) || n < 1 || n > 5) return undefined;
  return n;
}

/** Nur wenn Key gesetzt: null = Standard (2 Tage), 1–5 = Tage. */
function parseSlaDaysForUpdate(v) {
  if (v === null || v === '') return null;
  const n = Number(v);
  if (!Number.isInteger(n) || n < 1 || n > 5) return undefined;
  return n;
}

function slaDaysEqual(a, b) {
  const na = a == null || a === '' ? null : Number(a);
  const nb = b == null || b === '' ? null : Number(b);
  return na === nb;
}

const ticketListOrderBy = `
ORDER BY
  CASE WHEN t.status IN ('OPEN','WAITING') AND datetime('now') > ${ticketSlaDueExpr} THEN 0 ELSE 1 END ASC,
  CASE WHEN t.status IN ('OPEN','WAITING') AND datetime('now') > ${ticketSlaDueExpr} THEN ${ticketSlaDueExpr} ELSE '9999-12-31' END ASC,
  ${ticketLastActivityExpr} DESC`;

export function registerTicketRoutes(api) {
  api.get('/tickets', (req, res) => {
    const { status, priority, machineId, open } = req.query;
    const cond = ['1=1'];
    const params = [];
    const openFilter = open === '1' || open === 'true';
    if (openFilter) {
      cond.push("t.status IN ('OPEN', 'WAITING')");
    } else if (status) {
      cond.push('t.status = ?');
      params.push(status);
    }
    if (priority) {
      cond.push('t.priority = ?');
      params.push(priority);
    }
    if (machineId) {
      cond.push('t.machine_id = ?');
      params.push(machineId);
    }
    const sql = `${ticketJoinSelect} WHERE ${cond.join(' AND ')} ${ticketListOrderBy}`;
    const rows = db.prepare(sql).all(...params);
    res.json(rows.map(mapTicket));
  });

  api.post('/tickets', (req, res) => {
    const { machineId, title, description, status, priority, slaDays } =
      req.body || {};
    if (!machineId || !title || !description) {
      return badRequest(res, 'Pflichtfelder fehlen.');
    }
    const m = db
      .prepare('SELECT 1 AS ok FROM machines WHERE id = ?')
      .get(machineId);
    if (!m) return res.status(404).json({ message: 'Nicht gefunden' });
    const st = status || 'OPEN';
    const pr = priority || 'MEDIUM';
    const sd = parseSlaDaysForCreate(slaDays);
    if (sd === undefined) {
      return badRequest(res, 'slaDays ungültig (1–5 oder weglassen für Standard).');
    }
    const tid = randomUUID();
    db.prepare(
      `INSERT INTO tickets (id, machine_id, title, description, status, priority, sla_days, sla_anchor_at, updated_at)
     VALUES (?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'))`,
    ).run(tid, machineId, title, description, st, pr, sd);
    const full = db
      .prepare(`${ticketJoinSelect} WHERE t.id = ?`)
      .get(tid);
    res.status(201).json(mapTicket(full));
  });

  api.get('/tickets/:id/events', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const ex = db.prepare('SELECT 1 AS ok FROM tickets WHERE id = ?').get(id);
    if (!ex) return res.status(404).json({ message: 'Nicht gefunden' });
    const rows = db
      .prepare(
        `SELECT * FROM events WHERE ticket_id = ?
         ORDER BY CASE WHEN type = 'ATTACHMENT' THEN 1 ELSE 0 END ASC,
                  created_at DESC`,
      )
      .all(id);
    const eventIds = rows.map((r) => r.id);
    const byEvent = new Map();
    if (eventIds.length > 0) {
      const ph = eventIds.map(() => '?').join(',');
      const attRows = db
        .prepare(
          `SELECT * FROM ticket_attachments WHERE event_id IN (${ph}) ORDER BY created_at ASC`,
        )
        .all(...eventIds);
      for (const a of attRows) {
        if (!byEvent.has(a.event_id)) byEvent.set(a.event_id, []);
        byEvent.get(a.event_id).push(a);
      }
    }
    res.json(mergeAttachmentEventsForApi(rows, byEvent, db));
  });

  api.get('/tickets/:id', (req, res) => {
    const { id } = req.params;
    if (!UUID.test(id)) return res.status(404).json({ message: 'Nicht gefunden' });
    const row = db.prepare(`${ticketJoinSelect} WHERE t.id = ?`).get(id);
    if (!row) return res.status(404).json({ message: 'Nicht gefunden' });
    res.json(mapTicket(row));
  });

  api.put('/tickets/: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 tickets WHERE id = ?').get(id);
    if (!cur) return res.status(404).json({ message: 'Nicht gefunden' });
    const b = req.body || {};
    const next = {
      title: b.title ?? cur.title,
      description: b.description ?? cur.description,
      status: b.status ?? cur.status,
      priority: b.priority ?? cur.priority,
    };

    let nextSlaDays = cur.sla_days != null ? cur.sla_days : null;
    let resetSlaAnchor = false;
    if (Object.prototype.hasOwnProperty.call(b, 'slaDays')) {
      const parsed = parseSlaDaysForUpdate(b.slaDays);
      if (parsed === undefined) {
        return badRequest(
          res,
          'slaDays ungültig (1–5 oder leer für Standard).',
        );
      }
      nextSlaDays = parsed;
      resetSlaAnchor = !slaDaysEqual(parsed, cur.sla_days);
    }

    const lines = [];
    if (b.status !== undefined && b.status !== cur.status) {
      lines.push(`Status: ${cur.status} → ${b.status}`);
    }
    if (b.priority !== undefined && b.priority !== cur.priority) {
      lines.push(`Priorität: ${cur.priority} → ${b.priority}`);
    }
    if (b.title !== undefined && b.title !== cur.title) lines.push('Titel geändert');
    if (b.description !== undefined && b.description !== cur.description) {
      lines.push('Beschreibung geändert');
    }
    if (
      Object.prototype.hasOwnProperty.call(b, 'slaDays') &&
      !slaDaysEqual(nextSlaDays, cur.sla_days)
    ) {
      const label = (d) =>
        d == null ? 'Standard (2 Tage)' : `${d} Tag(e)`;
      lines.push(
        `Fälligkeit: ${label(cur.sla_days)} → ${label(nextSlaDays)}`,
      );
    }
    if (lines.length > 0) {
      const eid = randomUUID();
      db.prepare(
        `INSERT INTO events (id, ticket_id, type, description, callback_number, teamviewer_id, article_number, remote_duration_seconds, teamviewer_notes)
       VALUES (?, ?, 'SYSTEM', ?, NULL, NULL, NULL, NULL, NULL)`,
      ).run(eid, id, lines.join('; '));
    }

    db.prepare(
      `UPDATE tickets SET title = ?, description = ?, status = ?, priority = ?, sla_days = ?,
       sla_anchor_at = CASE WHEN ? THEN datetime('now') ELSE sla_anchor_at END,
       updated_at = datetime('now')
     WHERE id = ?`,
    ).run(
      next.title,
      next.description,
      next.status,
      next.priority,
      nextSlaDays,
      resetSlaAnchor ? 1 : 0,
      id,
    );

    const row = db.prepare(`${ticketJoinSelect} WHERE t.id = ?`).get(id);
    res.json(mapTicket(row));
  });
}
