<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Migrations;

use Doctrine\DBAL\Schema\Schema;
use Mautic\IntegrationsBundle\Migration\AbstractMigration;
use MauticPlugin\DealFlowBundle\Entity\Deal;
use MauticPlugin\DealFlowBundle\Entity\DealStage;
use MauticPlugin\DealFlowBundle\Entity\Pipeline;

/**
 * Migrates Pipeline, DealStage, and Deal from CommonEntity to FormEntity.
 *
 * Column renames:
 *   created_at → date_added, updated_at → date_modified
 *   is_active → is_published (Pipeline, DealStage)
 *   updated_by → modified_by (Deal)
 *
 * New columns required by FormEntity:
 *   created_by_user, modified_by, modified_by_user,
 *   checked_out, checked_out_by, checked_out_by_user, is_published
 */
final class Version_0_7_0 extends AbstractMigration
{
    protected function isApplicable(Schema $schema): bool
    {
        $tableName = $this->concatPrefix(Deal::TABLE_NAME);

        // Migration is applicable if deals table exists but lacks is_published
        // (column renames may have already run from a partial execution)
        return $schema->hasTable($tableName)
            && !$schema->getTable($tableName)->hasColumn('is_published');
    }

    protected function up(): void
    {
        $this->migratePipelines();
        $this->migrateStages();
        $this->migrateDeals();
    }

    private function migratePipelines(): void
    {
        $table = $this->concatPrefix(Pipeline::TABLE_NAME);
        $conn  = $this->entityManager->getConnection();
        $sm    = $conn->createSchemaManager();
        $cols  = $sm->introspectTable($table);

        if ($cols->hasColumn('created_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `created_at` `date_added` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('updated_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `updated_at` `date_modified` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('is_active')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `is_active` `is_published` TINYINT(1) NOT NULL DEFAULT 1',
                $table
            ));
        }

        $newColumns = [
            'created_by'           => 'INT NULL AFTER `is_published`',
            'created_by_user'      => 'VARCHAR(255) NULL AFTER `created_by`',
            'modified_by'          => 'INT NULL AFTER `created_by_user`',
            'modified_by_user'     => 'VARCHAR(255) NULL AFTER `modified_by`',
            'checked_out'          => 'DATETIME NULL AFTER `modified_by_user`',
            'checked_out_by'       => 'INT NULL AFTER `checked_out`',
            'checked_out_by_user'  => 'VARCHAR(255) NULL AFTER `checked_out_by`',
        ];

        foreach ($newColumns as $col => $def) {
            if (!$cols->hasColumn($col)) {
                $conn->executeStatement(sprintf(
                    'ALTER TABLE `%s` ADD `%s` %s',
                    $table,
                    $col,
                    $def
                ));
            }
        }
    }

    private function migrateStages(): void
    {
        $table = $this->concatPrefix(DealStage::TABLE_NAME);
        $conn  = $this->entityManager->getConnection();
        $sm    = $conn->createSchemaManager();
        $cols  = $sm->introspectTable($table);

        if ($cols->hasColumn('created_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `created_at` `date_added` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('updated_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `updated_at` `date_modified` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('is_active')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `is_active` `is_published` TINYINT(1) NOT NULL DEFAULT 1',
                $table
            ));
        }

        $newColumns = [
            'created_by'           => 'INT NULL AFTER `is_published`',
            'created_by_user'      => 'VARCHAR(255) NULL AFTER `created_by`',
            'modified_by'          => 'INT NULL AFTER `created_by_user`',
            'modified_by_user'     => 'VARCHAR(255) NULL AFTER `modified_by`',
            'checked_out'          => 'DATETIME NULL AFTER `modified_by_user`',
            'checked_out_by'       => 'INT NULL AFTER `checked_out`',
            'checked_out_by_user'  => 'VARCHAR(255) NULL AFTER `checked_out_by`',
        ];

        foreach ($newColumns as $col => $def) {
            if (!$cols->hasColumn($col)) {
                $conn->executeStatement(sprintf(
                    'ALTER TABLE `%s` ADD `%s` %s',
                    $table,
                    $col,
                    $def
                ));
            }
        }
    }

    private function migrateDeals(): void
    {
        $table = $this->concatPrefix(Deal::TABLE_NAME);
        $conn  = $this->entityManager->getConnection();
        $sm    = $conn->createSchemaManager();
        $cols  = $sm->introspectTable($table);

        if ($cols->hasColumn('created_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `created_at` `date_added` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('updated_at')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `updated_at` `date_modified` DATETIME NULL',
                $table
            ));
        }

        if ($cols->hasColumn('updated_by')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` CHANGE `updated_by` `modified_by` INT NULL',
                $table
            ));
        }

        // Re-introspect after renames so AFTER clauses use new column names
        $cols = $sm->introspectTable($table);

        if (!$cols->hasColumn('is_published')) {
            $conn->executeStatement(sprintf(
                'ALTER TABLE `%s` ADD `is_published` TINYINT(1) NOT NULL DEFAULT 1 AFTER `modified_by`',
                $table
            ));
        }

        $newColumns = [
            'created_by_user'      => 'VARCHAR(255) NULL AFTER `created_by`',
            'modified_by_user'     => 'VARCHAR(255) NULL AFTER `modified_by`',
            'checked_out'          => 'DATETIME NULL AFTER `modified_by_user`',
            'checked_out_by'       => 'INT NULL AFTER `checked_out`',
            'checked_out_by_user'  => 'VARCHAR(255) NULL AFTER `checked_out_by`',
        ];

        foreach ($newColumns as $col => $def) {
            if (!$cols->hasColumn($col)) {
                $conn->executeStatement(sprintf(
                    'ALTER TABLE `%s` ADD `%s` %s',
                    $table,
                    $col,
                    $def
                ));
            }
        }
    }
}
