export function parseJsonField(v) {
  if (v == null) return undefined;
  if (typeof v === 'object') return v;
  return JSON.parse(v);
}

export function mapMachine(r) {
  const o = {
    id: r.id,
    name: r.name,
    typ: r.typ,
    seriennummer: r.seriennummer,
    standort: r.standort,
    listStatus:
      r.list_status != null && String(r.list_status).trim() !== ''
        ? String(r.list_status).trim()
        : '',
    createdAt: r.created_at,
    updatedAt: r.updated_at,
  };
  if (r.extras != null && String(r.extras).trim() !== '') {
    try {
      o.extras =
        typeof r.extras === 'string' ? JSON.parse(r.extras) : r.extras;
    } catch {
      o.extras = null;
    }
  }
  return o;
}

export function mapTicket(r) {
  const machine_row = parseJsonField(r.machine_row);
  const t = {
    id: r.id,
    machineId: r.machine_id,
    title: r.title,
    description: r.description,
    status: r.status,
    priority: r.priority,
    slaDays: r.sla_days != null ? r.sla_days : null,
    slaAnchorAt: r.sla_anchor_at ?? null,
    dueAt: r.sla_due_at ?? null,
    isOverdue: Boolean(r.sla_is_overdue),
    createdAt: r.created_at,
    /** Letzte Änderung: neueres aus Ticket-Zeile oder letztem Event (für Anzeige „Aktualisiert“). */
    updatedAt: r.ticket_last_activity_at ?? r.updated_at,
  };
  if (machine_row) {
    t.machine = mapMachine(machine_row);
  }
  return t;
}

function mapAttachmentRow(a, ticketId) {
  return {
    id: a.id,
    originalName: a.original_name,
    mimeType: a.mime_type ?? null,
    sizeBytes: a.size_bytes,
    createdAt: a.created_at,
    url: `/tickets/${ticketId}/attachments/${a.id}/file`,
  };
}

export function mapEvent(r, attachmentRows = []) {
  const list = Array.isArray(attachmentRows) ? attachmentRows : [];
  return {
    id: r.id,
    ticketId: r.ticket_id,
    type: r.type,
    description: r.description,
    createdAt: r.created_at,
    callbackNumber: r.callback_number ?? null,
    teamviewerId: r.teamviewer_id ?? null,
    articleNumber: r.article_number ?? null,
    remoteDurationSeconds:
      r.remote_duration_seconds != null ? r.remote_duration_seconds : null,
    teamviewerNotes:
      r.teamviewer_notes != null && String(r.teamviewer_notes).trim() !== ''
        ? String(r.teamviewer_notes).trim()
        : null,
    attachments: list.map((a) => mapAttachmentRow(a, r.ticket_id)),
  };
}

export function mapPublicUser(r) {
  return {
    id: r.id,
    username: r.username,
    role: r.role,
    active: Boolean(r.active),
    source: r.source,
    ldapDn: r.ldap_dn || null,
    createdAt: r.created_at,
    updatedAt: r.updated_at,
  };
}

/** Spätester Start der Bearbeitungszeit: gespeicherter Anker oder letztes Nutzer-Event (ohne SYSTEM). */
export const ticketSlaActivityAnchorExpr = `MAX(
  datetime(COALESCE(t.sla_anchor_at, t.created_at)),
  COALESCE(
    (SELECT MAX(datetime(e.created_at)) FROM events e
     WHERE e.ticket_id = t.id AND e.type IN ('NOTE','CALL','REMOTE','PART','ATTACHMENT')),
    datetime(COALESCE(t.sla_anchor_at, t.created_at))
  )
)`;

export const ticketSlaDueExpr = `datetime((${ticketSlaActivityAnchorExpr}), '+' || CAST(COALESCE(t.sla_days, 2) AS TEXT) || ' days')`;

/** Spätester Zeitpunkt aus Ticket und Historie (alle Event-Typen). */
export const ticketLastActivityExpr = `MAX(
  datetime(t.updated_at),
  COALESCE(
    (SELECT MAX(datetime(e.created_at)) FROM events e WHERE e.ticket_id = t.id),
    datetime(t.updated_at)
  )
)`;

export const ticketJoinSelect = `
  SELECT t.*,
    ${ticketLastActivityExpr} AS ticket_last_activity_at,
    ${ticketSlaDueExpr} AS sla_due_at,
    (CASE WHEN t.status IN ('OPEN','WAITING') AND datetime('now') > ${ticketSlaDueExpr} THEN 1 ELSE 0 END) AS sla_is_overdue,
    json_object(
      'id', m.id,
      'name', m.name,
      'typ', m.typ,
      'seriennummer', m.seriennummer,
      'standort', m.standort,
      'list_status', m.list_status,
      'extras', m.extras,
      'created_at', m.created_at,
      'updated_at', m.updated_at
    ) AS machine_row
  FROM tickets t
  JOIN machines m ON m.id = t.machine_id`;
