<?php

declare(strict_types=1);

namespace DoctrineMigrations;

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

/**
 * Holds, recorded appointment segments, and the uniqueness guarantee.
 *
 * MariaDB has no range EXCLUDE constraint, so every occupied interval is broken into
 * fixed five-minute buckets and UNIQUE(resource_id, bucket_at, seat) makes an overlap
 * impossible. `seat` expresses capacity: a three-bed room has seats 0..2 and the
 * fourth concurrent booking finds nowhere to sit.
 *
 * Preventing double booking is the database's job, not the code's — any "is it free?"
 * check in PHP has a race window between the read and the write.
 */
final class Version20260731054324 extends AbstractMigration
{
    public function getDescription(): string
    {
        return 'Add appointment holds, recorded segments and occupancy buckets';
    }

    public function up(Schema $schema): void
    {
        $this->addSql('CREATE TABLE appointment_holds (id INT AUTO_INCREMENT NOT NULL, uuid VARCHAR(36) NOT NULL, starts_at INT NOT NULL, ends_at INT NOT NULL, expires_at INT NOT NULL, payload JSON NOT NULL, confirmed_at INT DEFAULT NULL, created_at INT NOT NULL, entity_type VARCHAR(10) NOT NULL, entity_id INT NOT NULL, user_id INT NOT NULL, UNIQUE INDEX UNIQ_6905A14BD17F50A6 (uuid), INDEX IDX_6905A14BA76ED395 (user_id), INDEX idx_hold_tenant (entity_type, entity_id), INDEX idx_hold_expiry (expires_at), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
        $this->addSql('CREATE TABLE appointment_segments (id INT AUTO_INCREMENT NOT NULL, sequence SMALLINT NOT NULL, name VARCHAR(150) NOT NULL, starts_at INT NOT NULL, ends_at INT NOT NULL, patient_present TINYINT DEFAULT 1 NOT NULL, appointment_id INT NOT NULL, INDEX IDX_13EA50E1E5B533F9 (appointment_id), INDEX idx_appointment_segment_seq (appointment_id, sequence), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
        $this->addSql('CREATE TABLE resource_occupancy_buckets (id INT AUTO_INCREMENT NOT NULL, bucket_at INT NOT NULL, seat SMALLINT NOT NULL, resource_id INT NOT NULL, occupancy_id INT NOT NULL, INDEX IDX_9BE4732989329D25 (resource_id), INDEX idx_bucket_occupancy (occupancy_id), UNIQUE INDEX uniq_bucket_resource_seat (resource_id, bucket_at, seat), PRIMARY KEY (id)) DEFAULT CHARACTER SET utf8mb4');
        $this->addSql('ALTER TABLE appointment_holds ADD CONSTRAINT FK_6905A14BA76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE appointment_segments ADD CONSTRAINT FK_13EA50E1E5B533F9 FOREIGN KEY (appointment_id) REFERENCES appointments (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE resource_occupancy_buckets ADD CONSTRAINT FK_9BE4732989329D25 FOREIGN KEY (resource_id) REFERENCES clinic_resources (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE resource_occupancy_buckets ADD CONSTRAINT FK_9BE473298A0BBA84 FOREIGN KEY (occupancy_id) REFERENCES resource_occupancy (id) ON DELETE CASCADE');
        $this->addSql('ALTER TABLE resource_occupancy ADD hold_id INT DEFAULT NULL');
    }

    public function down(Schema $schema): void
    {
        $this->addSql('ALTER TABLE appointment_holds DROP FOREIGN KEY FK_6905A14BA76ED395');
        $this->addSql('ALTER TABLE appointment_segments DROP FOREIGN KEY FK_13EA50E1E5B533F9');
        $this->addSql('ALTER TABLE resource_occupancy_buckets DROP FOREIGN KEY FK_9BE4732989329D25');
        $this->addSql('ALTER TABLE resource_occupancy_buckets DROP FOREIGN KEY FK_9BE473298A0BBA84');
        $this->addSql('DROP TABLE appointment_holds');
        $this->addSql('DROP TABLE appointment_segments');
        $this->addSql('DROP TABLE resource_occupancy_buckets');
        $this->addSql('ALTER TABLE resource_occupancy DROP hold_id');
    }
}
