import type { RowDataPacket, ResultSetHeader } from "mysql2";

import { db } from "@/src/lib/db";
import type {
  ArtMacchinaParte,
  ArticoloAssegnatoMacchina,
  AssegnaArticoloInput,
} from "@/src/types/artMacchineParti";

type ArtMacchinaParteRow = ArtMacchinaParte & RowDataPacket;

export async function getArticoliByMacchina(
  idmacchina: number
): Promise<ArticoloAssegnatoMacchina[]> {
  const [rows] = await db.execute<ArtMacchinaParteRow[]>(
    `SELECT 
      id, idart, descrizione, codice, barcode, 
      idmacchina, qtain, qtaout, created_at, updated_at
     FROM art_macchine_parti 
     WHERE idmacchina = :idmacchina`,
    { idmacchina }
  );

  return rows.map((row) => ({
    id: row.id,
    id_articolo: row.idart,
    id_macchina: row.idmacchina,
    descrizione: row.descrizione,
    codice: row.codice,
    barcode: row.barcode,
    quantita: Math.max(0, row.qtain - row.qtaout),
    qtain: row.qtain,
    qtaout: row.qtaout,
  }));
}

export async function assegnaArticoloMacchina(
  input: AssegnaArticoloInput
): Promise<{ success: boolean; message: string }> {
  const { idart, idmacchina, quantita, causale = "Assegnazione articolo", user = "system" } = input;

  const [articoloRows] = await db.execute<(RowDataPacket & { descrizione: string; codice: string; barcode: string | null })[]>(
    "SELECT descrizione, codice, barcode FROM articoli WHERE id = :idart LIMIT 1",
    { idart }
  );

  if (articoloRows.length === 0) {
    throw new Error("Articolo non trovato");
  }

  const articolo = articoloRows[0];

  const [macchinaRows] = await db.execute<(RowDataPacket & { nome: string })[]>(
    "SELECT nome FROM macchine_parti WHERE id = :idmacchina LIMIT 1",
    { idmacchina }
  );

  const macchinaNome = macchinaRows[0]?.nome || "Macchina";

  const [existingRows] = await db.execute<(RowDataPacket & { id: number })[]>(
    "SELECT id FROM art_macchine_parti WHERE idart = :idart AND idmacchina = :idmacchina LIMIT 1",
    { idart, idmacchina }
  );

  if (existingRows.length > 0) {
    await db.execute(
      `UPDATE art_macchine_parti 
       SET qtain = qtain + :quantita, updated_at = NOW() 
       WHERE idart = :idart AND idmacchina = :idmacchina`,
      { quantita, idart, idmacchina }
    );
  } else {
    await db.execute(
      `INSERT INTO art_macchine_parti (idart, descrizione, codice, barcode, idmacchina, qtain, qtaout) 
       VALUES (:idart, :descrizione, :codice, :barcode, :idmacchina, :quantita, 0)`,
      {
        idart,
        descrizione: articolo.descrizione,
        codice: articolo.codice,
        barcode: articolo.barcode,
        idmacchina,
        quantita,
      }
    );
  }

  await db.execute(
    `INSERT INTO movimenti (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente, id_macchina_parte, nome_macchina_parte)
     VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, 2, :causale, :utente, :id_macchina_parte, :nome_macchina_parte)`,
    {
      id_articolo: idart,
      articolo_codice: articolo.codice,
      articolo_descrizione: articolo.descrizione,
      quantita,
      causale,
      utente: user,
      id_macchina_parte: idmacchina,
      nome_macchina_parte: macchinaNome,
    }
  );

  return { success: true, message: "Articolo assegnato con successo" };
}

export async function rimuoviArticoloDaMacchina(
  idart: number,
  idmacchina: number,
  quantita: number,
  opzione: "carico" | "elimina",
  user: string = "system",
  idMagazzino?: number
): Promise<{ success: boolean; message: string }> {
  const [existingRows] = await db.execute<(RowDataPacket & { id: number; qtain: number; qtaout: number })[]>(
    "SELECT id, qtain, qtaout FROM art_macchine_parti WHERE idart = :idart AND idmacchina = :idmacchina LIMIT 1",
    { idart, idmacchina }
  );

  if (existingRows.length === 0) {
    throw new Error("Assegnazione non trovata");
  }

  const existing = existingRows[0];
  const quantitaDisponibile = existing.qtain - existing.qtaout;

  if (quantita > quantitaDisponibile) {
    throw new Error(`Quantità richiesta (${quantita}) supera quella disponibile (${quantitaDisponibile})`);
  }

  if (opzione === "carico" && !idMagazzino) {
    throw new Error("Seleziona un magazzino per il rientro");
  }

  await db.execute(
    `UPDATE art_macchine_parti 
     SET qtaout = qtaout + :quantita, updated_at = NOW() 
     WHERE idart = :idart AND idmacchina = :idmacchina`,
    { quantita, idart, idmacchina }
  );

  if (opzione === "carico" && idMagazzino) {
    const [articoloRows] = await db.execute<(RowDataPacket & { descrizione: string; codice: string })[]>(
      "SELECT descrizione, codice FROM articoli WHERE id = :idart LIMIT 1",
      { idart }
    );
    const articolo = articoloRows[0];

    await db.execute(
      `INSERT INTO movimenti (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente, id_magazzino_destinazione)
       VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, 1, :causale, :utente, :id_magazzino_destinazione)`,
      {
        id_articolo: idart,
        articolo_codice: articolo?.codice || "",
        articolo_descrizione: articolo?.descrizione || "",
        quantita,
        causale: "Rientro da macchina",
        utente: user,
        id_magazzino_destinazione: idMagazzino,
      }
    );

    // Aggiorna la giacenza nel magazzino
    const [existingGiacenza] = await db.execute<(RowDataPacket & { id: number; quantita: number })[]>(
      "SELECT id, quantita FROM giacenze WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId",
      { articoloId: idart, magazzinoId: idMagazzino }
    );

    if (existingGiacenza.length > 0) {
      await db.execute(
        "UPDATE giacenze SET quantita = quantita + :quantita WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId",
        { quantita, articoloId: idart, magazzinoId: idMagazzino }
      );
    } else {
      await db.execute(
        "INSERT INTO giacenze (articolo_id, id_magazzino, quantita) VALUES (:articoloId, :magazzinoId, :quantita)",
        { articoloId: idart, magazzinoId: idMagazzino, quantita }
      );
    }
  }

  return { success: true, message: opzione === "carico" ? "Articolo rientrato in magazzino" : "Articolo rimosso" };
}

export async function getArticoliPerTutteLeMacchine(): Promise<Record<number, ArticoloAssegnatoMacchina[]>> {
  const [rows] = await db.execute<ArtMacchinaParteRow[]>(
    `SELECT 
      id, idart, descrizione, codice, barcode, 
      idmacchina, qtain, qtaout, created_at, updated_at
     FROM art_macchine_parti 
     WHERE (qtain - qtaout) > 0`
  );

  const result: Record<number, ArticoloAssegnatoMacchina[]> = {};

  for (const row of rows) {
    const quantita = Math.max(0, row.qtain - row.qtaout);
    if (quantita <= 0) continue;

    if (!result[row.idmacchina]) {
      result[row.idmacchina] = [];
    }

    result[row.idmacchina].push({
      id: row.id,
      id_articolo: row.idart,
      id_macchina: row.idmacchina,
      descrizione: row.descrizione,
      codice: row.codice,
      barcode: row.barcode,
      quantita,
      qtain: row.qtain,
      qtaout: row.qtaout,
    });
  }

  return result;
}
