<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\Bridge\Doctrine\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

final class Version20260707113000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Permet d associer un frais de port a une categorie de livraison.';
    }

    public function up(Schema $schema): void
    {
        $schemaManager = $this->sm;

        if (!$schemaManager->tablesExist(['shop_shipping_price'])) {
            return;
        }

        $columns = $schemaManager->listTableColumns('shop_shipping_price');
        if (!isset($columns['delivery_method_category_id'])) {
            $this->addSql('ALTER TABLE shop_shipping_price ADD delivery_method_category_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\'');
        }

        $indexes = $schemaManager->listTableIndexes('shop_shipping_price');
        if (!$this->hasIndexOnColumns($indexes, ['delivery_method_category_id'])) {
            $this->addSql('CREATE INDEX IDX_SHIPPING_PRICE_DELIVERY_CATEGORY ON shop_shipping_price (delivery_method_category_id)');
        }

        $foreignKeys = $schemaManager->listTableForeignKeys('shop_shipping_price');
        if (!$this->hasForeignKey($foreignKeys, 'delivery_method_category_id', 'shop_delivery_method_category', 'id')) {
            $this->addSql('ALTER TABLE shop_shipping_price ADD CONSTRAINT FK_SHIPPING_PRICE_DELIVERY_CATEGORY FOREIGN KEY (delivery_method_category_id) REFERENCES shop_delivery_method_category (id)');
        }
    }

    public function down(Schema $schema): void
    {
        $schemaManager = $this->sm;

        if (!$schemaManager->tablesExist(['shop_shipping_price'])) {
            return;
        }

        $foreignKeys = $schemaManager->listTableForeignKeys('shop_shipping_price');
        $foreignKeyName = $this->getForeignKeyName($foreignKeys, 'delivery_method_category_id', 'shop_delivery_method_category', 'id');
        if (null !== $foreignKeyName) {
            $this->addSql(sprintf('ALTER TABLE shop_shipping_price DROP FOREIGN KEY %s', $foreignKeyName));
        }

        $indexes = $schemaManager->listTableIndexes('shop_shipping_price');
        $indexName = $this->getIndexNameOnColumns($indexes, ['delivery_method_category_id']);
        if (null !== $indexName) {
            $this->addSql(sprintf('DROP INDEX %s ON shop_shipping_price', $indexName));
        }

        $columns = $schemaManager->listTableColumns('shop_shipping_price');
        if (isset($columns['delivery_method_category_id'])) {
            $this->addSql('ALTER TABLE shop_shipping_price DROP delivery_method_category_id');
        }
    }

    private function hasIndexOnColumns(array $indexes, array $columns): bool
    {
        return null !== $this->getIndexNameOnColumns($indexes, $columns);
    }

    private function getIndexNameOnColumns(array $indexes, array $columns): ?string
    {
        $expectedColumns = array_map('strtolower', $columns);

        foreach ($indexes as $index) {
            if (array_map('strtolower', $index->getColumns()) === $expectedColumns) {
                return $index->getName();
            }
        }

        return null;
    }

    private function hasForeignKey(array $foreignKeys, string $localColumn, string $foreignTable, string $foreignColumn): bool
    {
        return null !== $this->getForeignKeyName($foreignKeys, $localColumn, $foreignTable, $foreignColumn);
    }

    private function getForeignKeyName(array $foreignKeys, string $localColumn, string $foreignTable, string $foreignColumn): ?string
    {
        foreach ($foreignKeys as $foreignKey) {
            if (
                array_map('strtolower', $foreignKey->getLocalColumns()) === [strtolower($localColumn)]
                && strtolower($foreignKey->getForeignTableName()) === strtolower($foreignTable)
                && array_map('strtolower', $foreignKey->getForeignColumns()) === [strtolower($foreignColumn)]
            ) {
                return $foreignKey->getName();
            }
        }

        return null;
    }
}
