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

import { db, getDbConnection } from "@/src/lib/db";
import type { Movimento, CreateMovimentoPayload } from "@/src/types/movimento";

type MovimentoRow = Movimento & RowDataPacket;

export async function listMovimenti(options: {
  articoloId?: number;
  magazzinoId?: number;
  limit?: number;
} = {}): Promise<Movimento[]> {
  const { articoloId, magazzinoId, limit = 100 } = options;

  const conditions: string[] = [];
  const params: Record<string, number> = {};

  if (articoloId) {
    conditions.push("m.id_articolo = :articoloId");
    params.articoloId = articoloId;
  }

  if (magazzinoId) {
    conditions.push("(m.id_magazzino_origine = :magazzinoId OR m.id_magazzino_destinazione = :magazzinoId)");
    params.magazzinoId = magazzinoId;
  }

  let query = `
    SELECT 
      m.id,
      m.id_articolo,
      m.articolo_codice,
      m.articolo_descrizione,
      m.id_macchina_parte,
      m.nome_macchina_parte,
      m.quantita,
      m.data_movimento,
      m.tipo_movimento,
      m.causale,
      m.utente,
      m.id_intervento,
      m.id_magazzino_origine,
      m.id_magazzino_destinazione,
      mo.descrizione AS magazzino_origine,
      md.descrizione AS magazzino_destinazione
    FROM movimenti m
    LEFT JOIN magazzini mo ON mo.id = m.id_magazzino_origine
    LEFT JOIN magazzini md ON md.id = m.id_magazzino_destinazione
  `;

  if (conditions.length > 0) {
    query += " WHERE " + conditions.join(" AND ");
  }

  query += ` ORDER BY m.data_movimento DESC LIMIT ${limit}`;

  const [rows] = await db.execute<MovimentoRow[]>(query, params);

  return rows.map((row) => ({
    id: row.id,
    id_articolo: row.id_articolo,
    articolo_codice: row.articolo_codice,
    articolo_descrizione: row.articolo_descrizione,
    id_macchina_parte: row.id_macchina_parte,
    nome_macchina_parte: row.nome_macchina_parte,
    quantita: Number(row.quantita) || 0,
    data_movimento: row.data_movimento,
    tipo_movimento: row.tipo_movimento,
    causale: row.causale,
    utente: row.utente,
    id_intervento: row.id_intervento,
    id_magazzino_origine: row.id_magazzino_origine,
    id_magazzino_destinazione: row.id_magazzino_destinazione,
    magazzino_origine: row.magazzino_origine,
    magazzino_destinazione: row.magazzino_destinazione,
  }));
}

export async function createMovimento(
  payload: CreateMovimentoPayload
): Promise<number> {
  const connection = await getDbConnection();

  try {
    await connection.beginTransaction();

    const [result] = await connection.execute<ResultSetHeader>(
      `INSERT INTO movimenti 
       (id_articolo, articolo_codice, articolo_descrizione, quantita, tipo_movimento, causale, utente, id_magazzino_origine, id_magazzino_destinazione)
       VALUES (:id_articolo, :articolo_codice, :articolo_descrizione, :quantita, :tipo_movimento, :causale, :utente, :id_magazzino_origine, :id_magazzino_destinazione)`,
      {
        id_articolo: payload.id_articolo,
        articolo_codice: payload.articolo_codice,
        articolo_descrizione: payload.articolo_descrizione,
        quantita: payload.quantita,
        tipo_movimento: payload.tipo_movimento,
        causale: payload.causale,
        utente: payload.utente,
        id_magazzino_origine: payload.id_magazzino_origine,
        id_magazzino_destinazione: payload.id_magazzino_destinazione,
      }
    );

    const movimentoId = result.insertId;

    const idMagazzino = payload.tipo_movimento === 1 
      ? payload.id_magazzino_destinazione 
      : payload.id_magazzino_origine;

    if (!idMagazzino) {
      throw new Error("Magazzino non specificato");
    }

    const [existingRows] = await connection.execute<RowDataPacket[]>(
      "SELECT id, quantita FROM giacenze WHERE articolo_id = :articoloId AND id_magazzino = :magazzinoId",
      { articoloId: payload.id_articolo, magazzinoId: idMagazzino }
    );

    const existing = existingRows[0];

    if (payload.tipo_movimento === 1) {
      if (existing) {
        await connection.execute<ResultSetHeader>(
          "UPDATE giacenze SET quantita = quantita + :quantita WHERE id = :id",
          { quantita: payload.quantita, id: existing.id }
        );
      } else {
        await connection.execute<ResultSetHeader>(
          "INSERT INTO giacenze (articolo_id, id_magazzino, quantita) VALUES (:articoloId, :magazzinoId, :quantita)",
          { articoloId: payload.id_articolo, magazzinoId: idMagazzino, quantita: payload.quantita }
        );
      }
    } else {
      if (!existing || Number(existing.quantita) < payload.quantita) {
        throw new Error(
          `Quantità insufficiente. Disponibile: ${existing?.quantita || 0}, richiesta: ${payload.quantita}`
        );
      }

      const nuovaQuantita = Number(existing.quantita) - payload.quantita;

      if (nuovaQuantita === 0) {
        await connection.execute<ResultSetHeader>(
          "DELETE FROM giacenze WHERE id = :id",
          { id: existing.id }
        );
      } else {
        await connection.execute<ResultSetHeader>(
          "UPDATE giacenze SET quantita = :quantita WHERE id = :id",
          { quantita: nuovaQuantita, id: existing.id }
        );
      }
    }

    await connection.commit();
    return movimentoId;
  } catch (error) {
    await connection.rollback();
    throw error;
  } finally {
    connection.release();
  }
}

export async function getMovimentoById(id: number): Promise<Movimento | null> {
  const [rows] = await db.execute<MovimentoRow[]>(
    `SELECT 
      m.*,
      mo.descrizione AS magazzino_origine,
      md.descrizione AS magazzino_destinazione
    FROM movimenti m
    LEFT JOIN magazzini mo ON mo.id = m.id_magazzino_origine
    LEFT JOIN magazzini md ON md.id = m.id_magazzino_destinazione
    WHERE m.id = :id`,
    { id }
  );

  const row = rows[0];
  if (!row) return null;

  return {
    id: row.id,
    id_articolo: row.id_articolo,
    articolo_codice: row.articolo_codice,
    articolo_descrizione: row.articolo_descrizione,
    id_macchina_parte: row.id_macchina_parte,
    nome_macchina_parte: row.nome_macchina_parte,
    quantita: Number(row.quantita) || 0,
    data_movimento: row.data_movimento,
    tipo_movimento: row.tipo_movimento,
    causale: row.causale,
    utente: row.utente,
    id_intervento: row.id_intervento,
    id_magazzino_origine: row.id_magazzino_origine,
    id_magazzino_destinazione: row.id_magazzino_destinazione,
    magazzino_origine: row.magazzino_origine,
    magazzino_destinazione: row.magazzino_destinazione,
  };
}
