<?php

namespace craft\migrations;

use Craft;
use craft\base\FieldInterface;
use craft\base\MergeableFieldInterface;
use craft\db\Migration;
use craft\fields\MissingField;
use craft\records\Field as FieldRecord;
use craft\services\Fields;

/**
 * Base field merge migration class.
 *
 * This is extended by content migrations generated by the `fields/merge` command.
 *
 * @since 5.3.0
 */
class BaseFieldMergeMigration extends Migration
{
    public string $persistingFieldUid;
    public string $outgoingFieldUid;

    public function safeUp(): bool
    {
        /** @var FieldRecord|null $persistingFieldRecord */
        $persistingFieldRecord = FieldRecord::findWithTrashed()
            ->where(['uid' => $this->persistingFieldUid])
            ->one();
        if (!$persistingFieldRecord) {
            echo "Couldn't find persisting field record ($this->persistingFieldUid)";
            return false;
        }

        /** @var MergeableFieldInterface|false $persistingField */
        $persistingField = $this->field($this->persistingFieldUid);
        if (!$persistingField) {
            return false;
        }

        /** @var MergeableFieldInterface|false $outgoingField */
        $outgoingField = $this->field($this->outgoingFieldUid);
        if (!$outgoingField) {
            return false;
        }

        $reason = null;
        if (!$outgoingField->canMergeInto($persistingField, $reason)) {
            echo sprintf(
                "%s (%s) doesn’t support merging into %s (%s)%s\n",
                $outgoingField->name,
                $outgoingField::displayName(),
                $persistingField->name,
                $persistingField::displayName(),
                $reason ? ": $reason" : '.',
            );
            return false;
        }

        $reason = null;
        if (!$persistingField->canMergeFrom($outgoingField, $reason)) {
            echo sprintf(
                "%s (%s) doesn’t support being merged into from %s (%s)%s\n",
                $persistingField->name,
                $persistingField::displayName(),
                $outgoingField->name,
                $outgoingField::displayName(),
                $reason ? ": $reason" : '.',
            );
            return false;
        }

        $outgoingField->afterMergeInto($persistingField);
        $persistingField->afterMergeFrom($outgoingField);

        return true;
    }

    private function field(string $uid): FieldInterface|false
    {
        $fieldsService = Craft::$app->getFields();
        $field = $fieldsService->getFieldByUid($uid);
        if (!$field) {
            // maybe it's soft-deleted
            /** @var FieldRecord|null $record */
            $record = FieldRecord::findWithTrashed()
                ->where(['uid' => $uid])
                ->one();

            if (!$record) {
                echo "Couldn't find field record $uid.\n";
                return false;
            }

            $field = $fieldsService->createField($record->toArray());
        }

        if ($field instanceof MissingField) {
            echo "$field->expectedType is missing.\n";
            return false;
        }

        if (!$field instanceof MergeableFieldInterface) {
            echo "$field->name doesn’t support merging.\n";
            return false;
        }

        return $field;
    }
}
