<?php

declare(strict_types=1);

namespace DoctrineMigrations;

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

/**
 * Record which environment a wallet transaction was entered in.
 *
 * The wallet itself stays the person's: the balance is derived from the sum of
 * that user's credits minus debits, across every environment. But the ledger is
 * also shown to a clinic through /api/v1/patient/{uuid}/wallet/transactions, and
 * without this column clinic A could read what the patient paid at clinic B,
 * down to the name of the staff member who entered it.
 *
 * The columns are deliberately NOT called entity_type/entity_id: TenantFilter
 * keys on those names and would then scope the balance query too, which would
 * show a patient half their own money. This is attribution, not ownership.
 *
 * Rows that cannot be attributed stay NULL and remain visible everywhere —
 * hiding them would make an existing patient's history look deleted.
 */
final class Version20260728160000 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Record the environment a wallet transaction was entered in, without scoping the balance';
    }

    public function up(Schema $schema): void
    {
        $this->connection->executeStatement(
            'ALTER TABLE wallet_transactions
             ADD recorded_entity_type VARCHAR(10) NULL, ADD recorded_entity_id INT NULL'
        );

        // Gateway-backed rows: the payment already carries the environment.
        $this->connection->executeStatement(
            'UPDATE wallet_transactions w JOIN payments p ON p.id = w.payment_id
             SET w.recorded_entity_type = p.entity_type, w.recorded_entity_id = p.entity_id
             WHERE w.payment_id IS NOT NULL'
        );

        // Desk payments taken against a visit: reference is "session:{uuid}".
        $this->connection->executeStatement(
            "UPDATE wallet_transactions w
             JOIN patient_sessions s ON s.uuid = SUBSTRING(w.reference, 9)
             JOIN patient_records r ON r.id = s.record_id
             SET w.recorded_entity_type = r.entity_type, w.recorded_entity_id = r.entity_id
             WHERE w.recorded_entity_type IS NULL AND w.reference LIKE 'session:%'"
        );

        $unattributed = (int) $this->connection->fetchOne(
            'SELECT COUNT(*) FROM wallet_transactions WHERE recorded_entity_type IS NULL'
        );
        $this->write(sprintf(
            '  wallet_transactions: %d row(s) could not be attributed to an environment and stay visible in all of them.',
            $unattributed,
        ));

        $this->connection->executeStatement(
            'CREATE INDEX idx_wallet_user_recorded_entity
             ON wallet_transactions (user_id, recorded_entity_type, recorded_entity_id)'
        );
    }

    public function down(Schema $schema): void
    {
        $this->addSql('DROP INDEX idx_wallet_user_recorded_entity ON wallet_transactions');
        $this->addSql('ALTER TABLE wallet_transactions DROP recorded_entity_type, DROP recorded_entity_id');
    }

    /** DDL on MariaDB commits implicitly; wrapping up() in a transaction would be a lie. */
    public function isTransactional(): bool
    {
        return false;
    }
}
