-- =============================================================================
-- cleanup_orphan_piece_refs.sql
-- =============================================================================
-- Contexte : la suppression directe de rows dans `pieces` (bypass Doctrine /
-- FK DB sans ON DELETE CASCADE) laisse des références orphelines dans plusieurs
-- tables, ce qui fait planter l'API au chargement d'une Machine :
--   Doctrine\ORM\EntityNotFoundException: Entity of type 'App\Entity\Piece' ...
--
-- Ce script fait deux choses :
--   1. ÉTAPE 1 (toujours exécutée) : compte les références orphelines par table
--      pour visualiser l'ampleur du problème.
--   2. ÉTAPE 2 (commentée par défaut) : insère un audit_log par row, puis
--      DELETE / UPDATE SET NULL selon la sémantique attendue côté entité.
--      Décommenter le bloc `BEGIN; ... COMMIT;` pour appliquer.
--
-- Usage :
--   # Dry-run (compte seulement)
--   psql -h <host> -U <user> -d inventory -f scripts/cleanup_orphan_piece_refs.sql
--
--   # Application : décommenter le bloc transactionnel en bas du fichier,
--   # puis relancer la même commande. La transaction garantit l'atomicité.
-- =============================================================================


-- ============================== ÉTAPE 1 : DRY-RUN ============================
\echo ''
\echo '=== Orphelins par table (Pieces) ==='

SELECT 'machine_piece_links'      AS table_name, count(*) AS orphans
  FROM machine_piece_links
  WHERE pieceid IS NOT NULL
    AND pieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'composant_piece_slots',     count(*)
  FROM composant_piece_slots
  WHERE selectedpieceid IS NOT NULL
    AND selectedpieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'piece_product_slots',       count(*)
  FROM piece_product_slots
  WHERE pieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'documents',                 count(*)
  FROM documents
  WHERE pieceid IS NOT NULL
    AND pieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'custom_field_values',       count(*)
  FROM custom_field_values
  WHERE pieceid IS NOT NULL
    AND pieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'piece_constructeur_links',  count(*)
  FROM piece_constructeur_links
  WHERE pieceid NOT IN (SELECT id FROM pieces)
UNION ALL
SELECT 'piece_products',            count(*)
  FROM piece_products
  WHERE piece_id NOT IN (SELECT id FROM pieces)
ORDER BY table_name;

\echo ''
\echo '=> Pour appliquer le cleanup, décommenter le bloc BEGIN/COMMIT ci-dessous.'
\echo ''


-- ============================== ÉTAPE 2 : APPLY =============================
-- Décommenter ce bloc pour exécuter le cleanup. La transaction garantit
-- l'atomicité : tout passe, ou rien (en cas d'erreur, ROLLBACK auto).
--
-- BEGIN;
--
-- -- 1. Audit log : snapshot des rows qui vont être supprimées (traçabilité prod).
--
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
-- SELECT
--   'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
--   'machine_piece_link',
--   l.id,
--   'delete',
--   json_build_object(
--     'id', l.id,
--     'machineId', l.machineid,
--     'pieceId', l.pieceid,
--     'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
--   ),
--   NULL,
--   NOW()
-- FROM machine_piece_links l
-- WHERE l.pieceid IS NOT NULL
--   AND l.pieceid NOT IN (SELECT id FROM pieces);
--
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
-- SELECT
--   'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
--   'piece_product_slot',
--   s.id,
--   'delete',
--   json_build_object(
--     'id', s.id,
--     'pieceId', s.pieceid,
--     'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
--   ),
--   NULL,
--   NOW()
-- FROM piece_product_slots s
-- WHERE s.pieceid NOT IN (SELECT id FROM pieces);
--
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
-- SELECT
--   'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
--   'document',
--   d.id,
--   'delete',
--   json_build_object(
--     'id', d.id,
--     'name', d.name,
--     'filename', d.filename,
--     'pieceId', d.pieceid,
--     'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
--   ),
--   NULL,
--   NOW()
-- FROM documents d
-- WHERE d.pieceid IS NOT NULL
--   AND d.pieceid NOT IN (SELECT id FROM pieces);
--
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
-- SELECT
--   'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
--   'custom_field_value',
--   v.id,
--   'delete',
--   json_build_object(
--     'id', v.id,
--     'pieceId', v.pieceid,
--     'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
--   ),
--   NULL,
--   NOW()
-- FROM custom_field_values v
-- WHERE v.pieceid IS NOT NULL
--   AND v.pieceid NOT IN (SELECT id FROM pieces);
--
-- INSERT INTO audit_logs (id, entitytype, entityid, action, snapshot, actorprofileid, createdat)
-- SELECT
--   'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
--   'piece_constructeur_link',
--   l.id,
--   'delete',
--   json_build_object(
--     'id', l.id,
--     'pieceId', l.pieceid,
--     'constructeurId', l.constructeurid,
--     'note', 'Cleaned by cleanup_orphan_piece_refs.sql - referenced piece no longer existed'
--   ),
--   NULL,
--   NOW()
-- FROM piece_constructeur_links l
-- WHERE l.pieceid NOT IN (SELECT id FROM pieces);
--
-- -- 2. Nettoyage des orphelins.
--
-- DELETE FROM machine_piece_links
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces);
--
-- UPDATE composant_piece_slots SET selectedpieceid = NULL
--   WHERE selectedpieceid IS NOT NULL
--     AND selectedpieceid NOT IN (SELECT id FROM pieces);
--
-- DELETE FROM piece_product_slots
--   WHERE pieceid NOT IN (SELECT id FROM pieces);
--
-- DELETE FROM documents
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces);
--
-- DELETE FROM custom_field_values
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces);
--
-- DELETE FROM piece_constructeur_links
--   WHERE pieceid NOT IN (SELECT id FROM pieces);
--
-- DELETE FROM piece_products
--   WHERE piece_id NOT IN (SELECT id FROM pieces);
--
-- -- 3. Vérification post-cleanup : tout doit être à 0.
-- SELECT 'machine_piece_links'      AS table_name, count(*) AS remaining_orphans
--   FROM machine_piece_links
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'composant_piece_slots',     count(*)
--   FROM composant_piece_slots
--   WHERE selectedpieceid IS NOT NULL
--     AND selectedpieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'piece_product_slots',       count(*)
--   FROM piece_product_slots
--   WHERE pieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'documents',                 count(*)
--   FROM documents
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'custom_field_values',       count(*)
--   FROM custom_field_values
--   WHERE pieceid IS NOT NULL
--     AND pieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'piece_constructeur_links',  count(*)
--   FROM piece_constructeur_links
--   WHERE pieceid NOT IN (SELECT id FROM pieces)
-- UNION ALL
-- SELECT 'piece_products',            count(*)
--   FROM piece_products
--   WHERE piece_id NOT IN (SELECT id FROM pieces)
-- ORDER BY table_name;
--
-- COMMIT;
