import type { RowDataPacket, ResultSetHeader } from "mysql2"; import { db } from "@/src/lib/db"; import type { Macchina, MacchinaWithDetails, MacchinaCreateInput, MacchinaUpdateInput, TipoMacchina, } from "@/src/types/macchina"; type MacchinaRow = Macchina & RowDataPacket; type MacchinaWithDetailsRow = MacchinaWithDetails & RowDataPacket; export async function listMacchine(): Promise { const [rows] = await db.execute(` SELECT m.id, m.nome, m.tipo, m.id_padre, m.id_stabilimento, m.colore_testo, s.descrizione AS stabilimento_nome, p.nome AS padre_nome FROM macchine_parti m LEFT JOIN stabilimenti s ON s.id = m.id_stabilimento LEFT JOIN macchine_parti p ON p.id = m.id_padre ORDER BY ISNULL(m.id_padre) DESC, m.id_padre ASC, m.nome ASC `); return rows.map((row) => ({ id: row.id, nome: row.nome, tipo: row.tipo as TipoMacchina, id_padre: row.id_padre, id_stabilimento: row.id_stabilimento, colore_testo: row.colore_testo || "#000000", stabilimento_nome: row.stabilimento_nome, padre_nome: row.padre_nome, })); } export async function getMacchinaById(id: number): Promise { const [rows] = await db.execute( `SELECT m.id, m.nome, m.tipo, m.id_padre, m.id_stabilimento, m.colore_testo, s.descrizione AS stabilimento_nome, p.nome AS padre_nome FROM macchine_parti m LEFT JOIN stabilimenti s ON s.id = m.id_stabilimento LEFT JOIN macchine_parti p ON p.id = m.id_padre WHERE m.id = :id LIMIT 1`, { id } ); const row = rows[0]; if (!row) return null; return { id: row.id, nome: row.nome, tipo: row.tipo as TipoMacchina, id_padre: row.id_padre, id_stabilimento: row.id_stabilimento, colore_testo: row.colore_testo || "#000000", stabilimento_nome: row.stabilimento_nome, padre_nome: row.padre_nome, }; } export async function createMacchina(input: MacchinaCreateInput): Promise { const { nome, tipo, id_padre, id_stabilimento, colore_testo = "#000000" } = input; let stabilimentoId = id_stabilimento; if (tipo === "macchina" && !id_padre && !stabilimentoId) { throw new Error("Stabilimento obbligatorio per macchine di livello root"); } if (id_padre && !stabilimentoId) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id_padre LIMIT 1", { id_padre } ); const parent = parentRows[0]; if (parent?.id_stabilimento) { stabilimentoId = parent.id_stabilimento; } } const [result] = await db.execute( `INSERT INTO macchine_parti (nome, tipo, id_padre, id_stabilimento, colore_testo) VALUES (:nome, :tipo, :id_padre, :id_stabilimento, :colore_testo)`, { nome, tipo, id_padre: id_padre || null, id_stabilimento: stabilimentoId || null, colore_testo, } ); return { id: result.insertId, nome, tipo, id_padre: id_padre || null, id_stabilimento: stabilimentoId || null, colore_testo, }; } export async function updateMacchina( id: number, input: MacchinaUpdateInput ): Promise { const current = await getMacchinaById(id); if (!current) return null; const nome = input.nome ?? current.nome; const tipo = input.tipo ?? current.tipo; const id_padre = input.id_padre !== undefined ? input.id_padre : current.id_padre; const colore_testo = input.colore_testo ?? current.colore_testo; let id_stabilimento = input.id_stabilimento !== undefined ? input.id_stabilimento : current.id_stabilimento; if (id_padre && !id_stabilimento) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id_padre LIMIT 1", { id_padre } ); const parent = parentRows[0]; if (parent?.id_stabilimento) { id_stabilimento = parent.id_stabilimento; } } await db.execute( `UPDATE macchine_parti SET nome = :nome, tipo = :tipo, id_padre = :id_padre, id_stabilimento = :id_stabilimento, colore_testo = :colore_testo WHERE id = :id`, { id, nome, tipo, id_padre: id_padre || null, id_stabilimento: id_stabilimento || null, colore_testo, } ); return getMacchinaById(id); } export async function deleteMacchina(id: number): Promise { const [childRows] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); if (childRows.length > 0) { for (const child of childRows) { await deleteMacchina(child.id); } } const [result] = await db.execute( "DELETE FROM macchine_parti WHERE id = :id", { id } ); return result.affectedRows > 0; } export async function cloneMacchina( id: number, newParentId: number | null = null ): Promise { const original = await getMacchinaById(id); if (!original) return null; let copyName = `${original.nome} (copia)`; let counter = 1; const [existingRows] = await db.execute( "SELECT nome FROM macchine_parti WHERE nome LIKE :pattern", { pattern: `${original.nome} (copia%` } ); const existingNames = new Set(existingRows.map((r) => r.nome)); while (existingNames.has(copyName)) { copyName = `${original.nome} (copia ${counter})`; counter++; } let stabilimentoId = original.id_stabilimento; if (newParentId) { const [parentRows] = await db.execute( "SELECT id_stabilimento FROM macchine_parti WHERE id = :id LIMIT 1", { id: newParentId } ); if (parentRows[0]?.id_stabilimento) { stabilimentoId = parentRows[0].id_stabilimento; } } const newMacchina = await createMacchina({ nome: copyName, tipo: original.tipo, id_padre: newParentId ?? original.id_padre, id_stabilimento: stabilimentoId, colore_testo: original.colore_testo, }); const [children] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); for (const child of children) { await cloneMacchina(child.id, newMacchina.id); } return newMacchina; } export async function getChildrenCount(id: number): Promise { const [rows] = await db.execute<(RowDataPacket & { count: number })[]>( "SELECT COUNT(*) as count FROM macchine_parti WHERE id_padre = :id", { id } ); return rows[0]?.count ?? 0; } export async function getAllDescendantIds(id: number): Promise { const ids: number[] = []; const [children] = await db.execute( "SELECT id FROM macchine_parti WHERE id_padre = :id", { id } ); for (const child of children) { ids.push(child.id); const descendantIds = await getAllDescendantIds(child.id); ids.push(...descendantIds); } return ids; }