-- ╔══════════════════════════════════════════════════════════════════╗
-- ║ CryptoWallet API — Production DB schema                          ║
-- ║                                                                    ║
-- ║ APPEND-ONLY / NON-DESTRUCTIVE:                                    ║
-- ║   Безопасно прогонять повторно. Ничего не DROP'ает, не overwrite. ║
-- ║   Если оператор добавил кастомные таблицы / индексы / constraints ║
-- ║   вручную — они НЕ будут затронуты.                                ║
-- ║                                                                    ║
-- ║ Применять: psql -h <host> -U <user> -d <db> -f cryptowallet-schema.sql ║
-- ╚══════════════════════════════════════════════════════════════════╝

-- NOTE: idempotency_keys и audit_log таблицы НЕ используются.
--   - idempotency_keys → KeyDB (Redis cache) — apps/api/src/config/redis.ts
--   - audit_log → stdout JSON-lines — apps/api/src/lib/audit-log.ts
-- Скрипт их НЕ дропает (чтобы re-run был non-destructive).
-- Если оператор хочет cleanup — manual one-time:
--   DROP TABLE IF EXISTS audit_log CASCADE;
--   DROP TABLE IF EXISTS idempotency_keys CASCADE;

-- ── USERS ───────────────────────────────────────────────────────────
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 (только ADD если нет колонки)
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 $$;

-- Constraint: blob size check (only ADDs if missing, никогда не DROP).
-- Floor 100 (worst-case 12-word 3-char mnemonic = 100 base64 chars).
-- Если оператор изменил этот constraint вручную — наш script его НЕ перезатрёт.
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'users_encrypted_mnemonic_size') THEN
        ALTER TABLE users
            ADD CONSTRAINT users_encrypted_mnemonic_size
            CHECK (encrypted_mnemonic IS NULL OR (char_length(encrypted_mnemonic) BETWEEN 100 AND 512));
    END IF;
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.
-- Это защита от unrecoverable fund loss при GDPR-wipe или admin удалении.
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);

-- NOTE: если БД старая и wallets.user_id_fkey ON DELETE CASCADE (а нужен RESTRICT
-- для защиты от fund loss при delete user), оператор делает manual ОДИН раз:
--
--   ALTER TABLE wallets DROP CONSTRAINT wallets_user_id_fkey;
--   ALTER TABLE wallets ADD CONSTRAINT wallets_user_id_fkey
--       FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT;
--
-- Этот script ничего не дропает — re-run полностью non-destructive.
