<?php

declare(strict_types=1);

namespace App\Command;

use DateTimeImmutable;
use Doctrine\DBAL\Connection;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;

#[AsCommand(
    name: 'app:convert-moteur-piece-to-component',
    description: 'Convertit la catégorie "Moteur" (PIECE) en COMPONENT et migre toutes les pièces liées en composants.',
)]
class ConvertMoteurPieceToComponentCommand extends Command
{
    private const MODEL_TYPE_ID = 'cmgytewe0002447ffup09bscr';

    public function __construct(
        private readonly Connection $connection,
    ) {
        parent::__construct();
    }

    protected function configure(): void
    {
        $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'Affiche les actions sans les exécuter');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $io     = new SymfonyStyle($input, $output);
        $dryRun = $input->getOption('dry-run');

        $io->title('Conversion catégorie "Moteur" : PIECE → COMPONENT');

        // ── 1. Vérifications ──────────────────────────────────────────────

        $modelType = $this->connection->fetchAssociative(
            'SELECT id, name, code, category FROM model_types WHERE id = :id',
            ['id' => self::MODEL_TYPE_ID],
        );

        if (!$modelType) {
            $io->error('ModelType "Moteur" introuvable (id: '.self::MODEL_TYPE_ID.')');

            return Command::FAILURE;
        }

        if ('PIECE' !== $modelType['category']) {
            $io->error(sprintf('Le ModelType "Moteur" est déjà de catégorie %s — rien à faire.', $modelType['category']));

            return Command::FAILURE;
        }

        $pieces = $this->connection->fetchAllAssociative(
            'SELECT id, name, reference FROM pieces WHERE typepieceid = :id ORDER BY name',
            ['id' => self::MODEL_TYPE_ID],
        );

        $pieceCount = count($pieces);
        $io->info(sprintf('Pièces à convertir : %d', $pieceCount));

        if ($pieceCount > 0) {
            $io->table(
                ['ID', 'Nom', 'Référence'],
                array_map(fn (array $p) => [$p['id'], $p['name'], $p['reference'] ?? '—'], $pieces),
            );
        }

        // Check blockers
        $blockers = [];

        $machineLinked = (int) $this->connection->fetchOne(
            'SELECT COUNT(*) FROM machine_piece_links mpl
             JOIN pieces p ON mpl.pieceid = p.id
             WHERE p.typepieceid = :id',
            ['id' => self::MODEL_TYPE_ID],
        );

        if ($machineLinked > 0) {
            $blockers[] = sprintf('%d pièce(s) liée(s) à des machines — conversion impossible.', $machineLinked);
        }

        $nameCollisions = $this->connection->fetchFirstColumn(
            'SELECT p.name FROM pieces p
             WHERE p.typepieceid = :id
             AND p.name IN (SELECT c.name FROM composants c)',
            ['id' => self::MODEL_TYPE_ID],
        );

        if ([] !== $nameCollisions) {
            $blockers[] = sprintf('Collision de noms avec des composants existants : %s', implode(', ', $nameCollisions));
        }

        $categoryCollision = (int) $this->connection->fetchOne(
            "SELECT COUNT(*) FROM model_types WHERE category = 'COMPONENT' AND name = :name AND id != :id",
            ['name' => $modelType['name'], 'id' => self::MODEL_TYPE_ID],
        );

        if ($categoryCollision > 0) {
            $blockers[] = sprintf('Un ModelType composant « %s » existe déjà.', $modelType['name']);
        }

        if ([] !== $blockers) {
            $io->error($blockers);

            return Command::FAILURE;
        }

        // Summary of related data
        $relatedCounts = $this->countRelatedData();
        $io->section('Données liées à migrer');
        $io->table(
            ['Table', 'Nombre'],
            array_map(fn (string $k, int $v) => [$k, $v], array_keys($relatedCounts), array_values($relatedCounts)),
        );

        if ($dryRun) {
            $io->warning('Mode dry-run : aucune modification effectuée.');

            return Command::SUCCESS;
        }

        // ── 2. Exécution ──────────────────────────────────────────────────

        $this->connection->beginTransaction();

        try {
            $now = new DateTimeImmutable()->format('Y-m-d H:i:s');

            // 2a. Copier les pièces dans composants
            $converted = $this->connection->executeStatement(
                'INSERT INTO composants (id, name, reference, referenceauto, description, prix, typecomposantid, productid, version, createdat, updatedat)
                 SELECT id, name, reference, referenceauto, description, prix, typepieceid, productid, version, createdat, :now
                 FROM pieces
                 WHERE typepieceid = :id',
                ['id' => self::MODEL_TYPE_ID, 'now' => $now],
            );
            $io->text(sprintf('✓ %d pièce(s) copiée(s) dans composants', $converted));

            // 2b. Transférer les documents
            $docs = $this->connection->executeStatement(
                'UPDATE documents SET composantid = pieceid, pieceid = NULL
                 WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d document(s) transféré(s)', $docs));

            // 2c. Transférer les custom_field_values
            $cfv = $this->connection->executeStatement(
                'UPDATE custom_field_values SET composantid = pieceid, pieceid = NULL
                 WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d valeur(s) de champs perso transférée(s)', $cfv));

            // 2d. Transférer les custom_fields (définitions)
            $cf = $this->connection->executeStatement(
                'UPDATE custom_fields SET typecomposantid = typepieceid, typepieceid = NULL
                 WHERE typepieceid = :id',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d définition(s) de champs perso transférée(s)', $cf));

            // 2e. Transférer les constructeur links
            $ctorLinks = $this->connection->executeStatement(
                "INSERT INTO composant_constructeur_links (id, composantid, constructeurid, supplierreference, createdat, updatedat)
                 SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
                        pcl.pieceid, pcl.constructeurid, pcl.supplierreference, pcl.createdat, :now
                 FROM piece_constructeur_links pcl
                 WHERE pcl.pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)",
                ['id' => self::MODEL_TYPE_ID, 'now' => $now],
            );

            if ($ctorLinks > 0) {
                $this->connection->executeStatement(
                    'DELETE FROM piece_constructeur_links
                     WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                    ['id' => self::MODEL_TYPE_ID],
                );
            }
            $io->text(sprintf('✓ %d lien(s) constructeur transféré(s)', $ctorLinks));

            // 2f. Convertir composant_piece_slots → composant_subcomponent_slots
            $slots = $this->connection->executeStatement(
                "INSERT INTO composant_subcomponent_slots (id, composantid, alias, familycode, typecomposantid, selectedcomposantid, position, createdat, updatedat)
                 SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
                        cps.composantid,
                        COALESCE(sp.name, 'Moteur'),
                        'moteur',
                        cps.typepieceid,
                        cps.selectedpieceid,
                        cps.position,
                        cps.createdat,
                        :now
                 FROM composant_piece_slots cps
                 LEFT JOIN pieces sp ON sp.id = cps.selectedpieceid
                 WHERE cps.typepieceid = :id",
                ['id' => self::MODEL_TYPE_ID, 'now' => $now],
            );

            $this->connection->executeStatement(
                'DELETE FROM composant_piece_slots WHERE typepieceid = :id',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d slot(s) pièce convertis en slots sous-composant', $slots));

            // 2g. Convertir skeleton_piece_requirements → skeleton_subcomponent_requirements
            $skelReqs = $this->connection->executeStatement(
                "INSERT INTO skeleton_subcomponent_requirements (id, modeltypeid, alias, familycode, typecomposantid, position, createdat, updatedat)
                 SELECT 'cl' || substring(md5(random()::text || clock_timestamp()::text), 1, 24),
                        spr.modeltypeid,
                        'Moteur',
                        'moteur',
                        spr.typepieceid,
                        spr.position,
                        spr.createdat,
                        :now
                 FROM skeleton_piece_requirements spr
                 WHERE spr.typepieceid = :id",
                ['id' => self::MODEL_TYPE_ID, 'now' => $now],
            );

            $this->connection->executeStatement(
                'DELETE FROM skeleton_piece_requirements WHERE typepieceid = :id',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d skeleton requirement(s) convertis', $skelReqs));

            // 2h. Mettre à jour audit_logs entity_type
            $auditUpdated = $this->connection->executeStatement(
                "UPDATE audit_logs SET entitytype = 'composant'
                 WHERE entitytype = 'piece'
                 AND entityid IN (SELECT id FROM pieces WHERE typepieceid = :id)",
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d audit log(s) mis à jour', $auditUpdated));

            // 2i. Mettre à jour comments entity_type
            $commentsUpdated = $this->connection->executeStatement(
                "UPDATE comments SET entity_type = 'composant'
                 WHERE entity_type = 'piece'
                 AND entity_id IN (SELECT id FROM pieces WHERE typepieceid = :id)",
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d commentaire(s) mis à jour', $commentsUpdated));

            // 2j. Supprimer les pièces originales
            $deleted = $this->connection->executeStatement(
                'DELETE FROM pieces WHERE typepieceid = :id',
                ['id' => self::MODEL_TYPE_ID],
            );
            $io->text(sprintf('✓ %d pièce(s) supprimée(s)', $deleted));

            // 2k. Changer la catégorie du ModelType
            $this->connection->executeStatement(
                "UPDATE model_types SET category = 'COMPONENT', updatedat = :now WHERE id = :id",
                ['id' => self::MODEL_TYPE_ID, 'now' => $now],
            );
            $io->text('✓ ModelType "Moteur" passé en COMPONENT');

            $this->connection->commit();

            $io->success(sprintf('Conversion terminée : %d pièces → composants.', $converted));

            return Command::SUCCESS;
        } catch (Throwable $e) {
            $this->connection->rollBack();
            $io->error('Erreur — rollback effectué : '.$e->getMessage());

            return Command::FAILURE;
        }
    }

    /**
     * @return array<string, int>
     */
    private function countRelatedData(): array
    {
        $id = self::MODEL_TYPE_ID;

        return [
            'pieces' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM pieces WHERE typepieceid = :id',
                ['id' => $id],
            ),
            'documents' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM documents WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                ['id' => $id],
            ),
            'custom_field_values' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM custom_field_values WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                ['id' => $id],
            ),
            'custom_fields' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM custom_fields WHERE typepieceid = :id',
                ['id' => $id],
            ),
            'piece_constructeur_links' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM piece_constructeur_links WHERE pieceid IN (SELECT id FROM pieces WHERE typepieceid = :id)',
                ['id' => $id],
            ),
            'composant_piece_slots (→ subcomponent_slots)' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM composant_piece_slots WHERE typepieceid = :id',
                ['id' => $id],
            ),
            'skeleton_piece_requirements (→ subcomponent_reqs)' => (int) $this->connection->fetchOne(
                'SELECT COUNT(*) FROM skeleton_piece_requirements WHERE typepieceid = :id',
                ['id' => $id],
            ),
        ];
    }
}
