-- ╔══════════════════════════════════════════════════════════════════╗
-- ║ CryptoWallet API — Production DB schema (idempotent, custodial)  ║
-- ║ Применять: psql -h <host> -U postgres_user -d postgres -f ...     ║
-- ║ Безопасно прогонять повторно на existing БД.                      ║
-- ╚══════════════════════════════════════════════════════════════════╝

-- NOTE: idempotency_keys + audit_log таблицы УДАЛЕНЫ из БД.
--   - idempotency_keys → KeyDB (Redis cache), см. apps/api/src/config/redis.ts
--   - audit_log → stdout-only (Docker logs / log-aggregator подбирает JSON lines)
-- Migration ниже drop'ает их если они существуют от прошлой версии.

CREATE TABLE IF NOT EXISTS users (
    id                  VARCHAR(26)               NOT NULL PRIMARY KEY,
    email               VARCHAR(255)              NOT NULL UNIQUE,
    password_hash       VARCHAR(255)              NOT NULL,
    last_name           VARCHAR(128),
    first_name          VARCHAR(128),
    middle_name         VARCHAR(128),
    birth_date          DATE,
    -- DEPRECATED: исторически generic crypto address; для ETH используем erc20 ниже.
    crypto_wallet       VARCHAR(255),
    phone               VARCHAR(16),
    inn                 VARCHAR(12),
    kyc_verified        BOOLEAN                   NOT NULL DEFAULT FALSE,
    kyc_verified_at     TIMESTAMP WITH TIME ZONE,
    is_deleted          BOOLEAN                   NOT NULL DEFAULT FALSE,
    created_at          TIMESTAMP WITH TIME ZONE  NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at          TIMESTAMP WITH TIME ZONE  NOT NULL DEFAULT CURRENT_TIMESTAMP,
    passport_data       VARCHAR(255),
    erc20               VARCHAR(255),
    -- EXTENSION (custodial wallet support):
    -- AES-256-GCM blob: IV(12) || ciphertext || authTag(16), base64. Master-key в Vault.
    encrypted_mnemonic  TEXT
);

-- Idempotent ALTERs для existing БД без extension-columns
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'encrypted_mnemonic') THEN
        ALTER TABLE users ADD COLUMN encrypted_mnemonic TEXT;
    END IF;
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'erc20') THEN
        ALTER TABLE users ADD COLUMN erc20 VARCHAR(255);
    END IF;
    IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'users' AND column_name = 'passport_data') THEN
        ALTER TABLE users ADD COLUMN passport_data VARCHAR(255);
    END IF;
END $$;

-- Sanity check на blob size. Floor 100 (worst-case 12-word всё-3char mnemonic):
DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_encrypted_mnemonic_size') THEN
        ALTER TABLE users DROP CONSTRAINT users_encrypted_mnemonic_size;
    END IF;
    ALTER TABLE users
        ADD CONSTRAINT users_encrypted_mnemonic_size
        CHECK (encrypted_mnemonic IS NULL OR (char_length(encrypted_mnemonic) BETWEEN 100 AND 512));
END $$;

-- Case-insensitive email uniqueness (Alice@x.com ≠ alice@x.com → ACCOUNT HIJACKING fix)
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_indexes WHERE indexname = 'users_email_lower_unique') THEN
        CREATE UNIQUE INDEX users_email_lower_unique ON users (lower(email));
    END IF;
END $$;

-- Partial index для active-user queries
CREATE INDEX IF NOT EXISTS idx_users_active ON users(id) WHERE is_deleted = FALSE;

-- erc20 format check (NULL or 0x + 40 hex)
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_erc20_format') THEN
        ALTER TABLE users
            ADD CONSTRAINT users_erc20_format
            CHECK (erc20 IS NULL OR erc20 ~ '^0x[0-9a-fA-F]{40}$');
    END IF;
END $$;

-- KYC consistency: verified=true requires verified_at NOT NULL
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_kyc_consistency') THEN
        ALTER TABLE users
            ADD CONSTRAINT users_kyc_consistency
            CHECK ((kyc_verified = FALSE) OR (kyc_verified = TRUE AND kyc_verified_at IS NOT NULL));
    END IF;
END $$;

-- ── WALLETS ─────────────────────────────────────────────────────────
-- ON DELETE RESTRICT: hard-delete user → запрос отвергнут пока есть wallets.
CREATE TABLE IF NOT EXISTS wallets (
    id                  VARCHAR(26)               NOT NULL PRIMARY KEY,
    user_id             VARCHAR(26)               NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
    chain               VARCHAR(16)               NOT NULL,
    address             VARCHAR(128)              NOT NULL,
    derivation_path     VARCHAR(64)               NOT NULL,
    created_at          TIMESTAMP WITH TIME ZONE  NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (user_id, chain),
    CHECK (chain IN ('ETH', 'BSC', 'BTC', 'TRX', 'SOL'))
);

CREATE INDEX IF NOT EXISTS idx_wallets_user_id ON wallets(user_id);
CREATE INDEX IF NOT EXISTS idx_wallets_address ON wallets(address);

-- Idempotent FK migration: если raised на старой DB с CASCADE — поменять
DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.referential_constraints
        WHERE constraint_name LIKE 'wallets_user_id_fkey%' AND delete_rule = 'CASCADE'
    ) THEN
        ALTER TABLE wallets DROP CONSTRAINT IF EXISTS wallets_user_id_fkey;
        ALTER TABLE wallets ADD CONSTRAINT wallets_user_id_fkey
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
    END IF;
END $$;

-- ── DROP legacy tables (если existing БД от прошлой версии) ────────
-- idempotency_keys → KeyDB cache (apps/api/src/lib/idempotency.ts → Redis)
-- audit_log → stdout-only (apps/api/src/lib/audit-log.ts)
DROP TABLE IF EXISTS audit_log CASCADE;
DROP TABLE IF EXISTS idempotency_keys CASCADE;
