<?php

namespace MediaWiki\Extension\CustomQueryPage\Store;

use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\IDatabase;
use Wikimedia\Rdbms\ILoadBalancer;
use const DB_PRIMARY;
use const DB_REPLICA;
use function property_exists;
use function trim;

class RoutineCloner {
    private ILoadBalancer $loadBalancer;

    public function __construct( ILoadBalancer $loadBalancer ) {
        $this->loadBalancer = $loadBalancer;
    }

    /**
     * @throws RoutineCloneException
     */
    public function cloneTag( string $sourceTag, string $targetTag, ?string $deployComment, UserIdentity $user ): RoutineCloneResult {
        $sourceTag = trim( $sourceTag );
        $targetTag = trim( $targetTag );

        if ( $sourceTag === '' ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-source-empty' );
        }
        if ( $targetTag === '' ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-target-empty' );
        }
        if ( $sourceTag === $targetTag ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-same-tag', [ $sourceTag ] );
        }

        $dbr = $this->loadBalancer->getConnectionRef( DB_REPLICA );
        $sourceRow = $dbr->selectRow( 'tags', '*', [ 'tag_id' => $sourceTag ], __METHOD__ );
        if ( !$sourceRow ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-source-missing', [ $sourceTag ] );
        }

        $existingTarget = $dbr->selectField( 'tags', 'tag_id', [ 'tag_id' => $targetTag ], __METHOD__ );
        if ( $existingTarget ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-target-exists', [ $targetTag ] );
        }

        $counts = $dbr->selectRow(
            'tag_pages',
            [
                'rev_count' => 'COUNT(*)',
                'page_count' => 'COUNT(DISTINCT tag_page)'
            ],
            [ 'tag_id' => $sourceTag ],
            __METHOD__
        );
        if ( !$counts || !(int)$counts->rev_count ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-source-no-pages', [ $sourceTag ] );
        }

        $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
        $tagData = $this->buildTagRow( $sourceRow, $targetTag, $deployComment );
        try {
            $dbw->insert( 'tags', $tagData, __METHOD__ );
        } catch ( \Throwable $e ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-generic', [], $e );
        }

        try {
            $this->copyTagPages( $dbw, $sourceTag, $targetTag );
        } catch ( \Throwable $e ) {
            throw new RoutineCloneException( 'customquerypage-clone-error-generic', [], $e );
        }

        return new RoutineCloneResult( (int)$counts->rev_count, (int)$counts->page_count );
    }

    private function buildTagRow( object $sourceRow, string $targetTag, ?string $deployComment ): array {
        $data = [ 'tag_id' => $targetTag ];

        foreach ( [ 'publish', 'locked', 'deleted', 'comment' ] as $field ) {
            if ( property_exists( $sourceRow, $field ) ) {
                $data[$field] = $sourceRow->$field;
            }
        }

        if ( $deployComment !== null ) {
            $data['comment'] = $deployComment === '' ? null : $deployComment;
        }

        if ( property_exists( $sourceRow, 'tag_user' ) ) {
            $data['tag_user'] = $sourceRow->tag_user;
        }
        if ( property_exists( $sourceRow, 'tag_user_text' ) ) {
            $data['tag_user_text'] = $sourceRow->tag_user_text;
        }
        if ( property_exists( $sourceRow, 'tag_timestamp' ) ) {
            $data['tag_timestamp'] = $sourceRow->tag_timestamp;
        }

        return $data;
    }

    private function copyTagPages( IDatabase $db, string $sourceTag, string $targetTag ): void {
        $db->insertSelect(
            'tag_pages',
            'tag_pages',
            [
                'tag_id' => $db->addQuotes( $targetTag ),
                'tag_page' => 'tag_page',
                'tag_rev' => 'tag_rev'
            ],
            [ 'tag_id' => $sourceTag ],
            __METHOD__
        );
    }
}
