-- 002__working.sql
-- Рабочая схема fansy. Данные сюда переливаются из fansy_staging после
-- валидации. Сервисы Bridge-and-Join-s читают только эту схему.

SET search_path TO fansy, public;

-- ---------------------------------------------------------------------
-- participants — справочник участников сервиса MOST (контрагенты M2M)
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS participants (
    inn                          varchar(10) PRIMARY KEY,
    ogrn                         varchar(15),
    full_name_rus                text NOT NULL,
    short_name_rus               text,
    display_name_rus             text NOT NULL,
    full_name_eng                text,
    short_name_eng               text,
    display_name_eng             text,
    depository_participant_code  varchar(12),
    broker_participant_code      varchar(12),
    is_available_for_m2m         boolean NOT NULL DEFAULT false,
    comment                      text,
    created_at                   timestamptz NOT NULL DEFAULT now(),
    updated_at                   timestamptz NOT NULL DEFAULT now(),
    CHECK (inn ~ '^[0-9]{10}$'),
    CHECK (depository_participant_code IS NULL OR depository_participant_code ~ '^[A-Z0-9]+$'),
    CHECK (broker_participant_code IS NULL OR broker_participant_code ~ '^[A-Z0-9]+$')
);
COMMENT ON TABLE participants IS 'Справочник участников сервиса MOST: депозитарии и брокеры, между которыми идут M2M-переводы.';
COMMENT ON COLUMN participants.inn IS 'ИНН юрлица (10 цифр), первичный ключ.';
COMMENT ON COLUMN participants.depository_participant_code IS 'Код участника M2M на стороне депозитария (для DepositoryPlace в M2MTransferHandbook).';
COMMENT ON COLUMN participants.broker_participant_code IS 'Код участника M2M на стороне брокера (для BrokerPlace).';
COMMENT ON COLUMN participants.is_available_for_m2m IS 'Готовность участника принимать/отправлять M2M-сообщения (включается после подписания НРД-договора).';

CREATE INDEX IF NOT EXISTS idx_participants_dep_code ON participants(depository_participant_code) WHERE depository_participant_code IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_participants_brk_code ON participants(broker_participant_code) WHERE broker_participant_code IS NOT NULL;

-- ---------------------------------------------------------------------
-- securities — справочник ценных бумаг
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS securities (
    security_code     char(12) PRIMARY KEY,
    isin              char(12),
    classification    varchar(4),
    category          varchar(4),
    security_type     varchar(256),
    security_series   text,
    reg_number        varchar(256),
    fund_class        varchar(120),
    display_name      text NOT NULL,
    created_at        timestamptz NOT NULL DEFAULT now(),
    updated_at        timestamptz NOT NULL DEFAULT now(),
    CHECK (security_code ~ '^[0-9A-Z_/-]+$'),
    CHECK (isin IS NULL OR isin ~ '^[A-Z]{2}[A-Z0-9]{9}[0-9]$'),
    CHECK (classification IS NULL OR classification IN ('BOND', 'SHAR', 'MFUN')),
    CHECK (category IS NULL OR category IN ('ORDN', 'PREF', 'UKWN'))
);
COMMENT ON TABLE securities IS 'Справочник ценных бумаг с их идентификаторами и классификацией.';
COMMENT ON COLUMN securities.security_code IS 'Идентификатор ценной бумаги в системе НРД (XSD SecurityCodeType).';
COMMENT ON COLUMN securities.classification IS 'Тип ценной бумаги: BOND (облигация), SHAR (акция), MFUN (ПИФ).';
COMMENT ON COLUMN securities.category IS 'Категория акций: ORDN (обыкновенные), PREF (привилегированные), UKWN (неизвестно).';
COMMENT ON COLUMN securities.reg_number IS 'Регистрационный номер выпуска (для акций и облигаций) или регномер правил доверительного управления ПИФ.';
COMMENT ON COLUMN securities.fund_class IS 'Класс паёв ПИФа (если применимо).';

CREATE INDEX IF NOT EXISTS idx_securities_isin ON securities(isin) WHERE isin IS NOT NULL;

-- ---------------------------------------------------------------------
-- clients — депоненты / инвесторы
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS clients (
    id           uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inn          varchar(12),
    last_name    varchar(50) NOT NULL,
    first_name   varchar(50) NOT NULL,
    middle_name  varchar(50),
    birth_date   date,
    created_at   timestamptz NOT NULL DEFAULT now(),
    updated_at   timestamptz NOT NULL DEFAULT now(),
    CHECK (inn IS NULL OR inn ~ '^[0-9]{10,12}$')
);
COMMENT ON TABLE clients IS 'Депоненты-физлица. Привязка к документам и счетам — через FK из дочерних таблиц.';
COMMENT ON COLUMN clients.inn IS 'ИНН физлица (12 цифр) или организации (10 цифр), опционально.';
COMMENT ON COLUMN clients.last_name IS 'Фамилия (XSD String50, обязательно).';
COMMENT ON COLUMN clients.first_name IS 'Имя (XSD String50, обязательно).';
COMMENT ON COLUMN clients.middle_name IS 'Отчество (XSD String50, опционально).';

CREATE INDEX IF NOT EXISTS idx_clients_inn ON clients(inn) WHERE inn IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_clients_lastname ON clients(last_name, first_name);

-- ---------------------------------------------------------------------
-- client_documents — документы инвестора
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS client_documents (
    id             uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id      uuid NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
    document_type  varchar(2) NOT NULL,
    series         text,
    number         text NOT NULL,
    issued_at      date,
    issuer         text,
    created_at     timestamptz NOT NULL DEFAULT now(),
    CHECK (document_type IN (
        '01','02','03','04','05','06','07','09','10','11','12','13','14',
        '21','22','23','26','27','91'
    )),
    CHECK (series IS NULL OR series ~ '^\S+$'),
    CHECK (number ~ '^\S+$')
);
COMMENT ON TABLE client_documents IS 'Документы, удостоверяющие личность инвестора. Коды по справочнику НРД (XSD IdentityDocumentCodeEnum).';
COMMENT ON COLUMN client_documents.document_type IS 'Код вида документа (01..91, см. XSD НРД).';
COMMENT ON COLUMN client_documents.series IS 'Серия документа (без пробелов).';
COMMENT ON COLUMN client_documents.number IS 'Номер документа (без пробелов, обязательно).';

CREATE INDEX IF NOT EXISTS idx_client_documents_client ON client_documents(client_id);

-- ---------------------------------------------------------------------
-- iia_contracts — договоры ИИС
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS iia_contracts (
    id                uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id         uuid NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
    agreement_type    varchar(3) NOT NULL,
    agreement_number  varchar(128) NOT NULL,
    agreement_date    date NOT NULL,
    broker_inn        varchar(10) NOT NULL,
    created_at        timestamptz NOT NULL DEFAULT now(),
    CHECK (agreement_type IN ('T12', 'T03')),
    CHECK (broker_inn ~ '^[0-9]{10}$')
);
COMMENT ON TABLE iia_contracts IS 'Договоры на ведение ИИС инвестора.';
COMMENT ON COLUMN iia_contracts.agreement_type IS 'Тип договора: T12 — ИИС-1/ИИС-2 (старый формат); T03 — ИИС-3 (новый).';
COMMENT ON COLUMN iia_contracts.broker_inn IS 'ИНН брокера, с которым заключён договор ИИС.';

CREATE INDEX IF NOT EXISTS idx_iia_contracts_client ON iia_contracts(client_id);
CREATE INDEX IF NOT EXISTS idx_iia_contracts_broker ON iia_contracts(broker_inn);

-- ---------------------------------------------------------------------
-- settlement_requisites — реквизиты расчётов (депозитарии)
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS settlement_requisites (
    id            uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    inn           varchar(10) NOT NULL UNIQUE,
    display_name  text NOT NULL,
    created_at    timestamptz NOT NULL DEFAULT now(),
    CHECK (inn ~ '^[0-9]{10}$')
);
COMMENT ON TABLE settlement_requisites IS 'Реквизиты передающего и принимающего депозитариев (XSD SettlementRequisitesType — содержит только ИНН).';

-- ---------------------------------------------------------------------
-- depo_accounts — депо-счета и разделы
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS depo_accounts (
    id              uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id       uuid NOT NULL REFERENCES clients(id) ON DELETE RESTRICT,
    deponent_code   varchar(50) NOT NULL,
    account_id      varchar(50) NOT NULL,
    section_id      varchar(50) NOT NULL,
    depository_inn  varchar(10) NOT NULL,
    is_active       boolean NOT NULL DEFAULT true,
    is_trading      boolean NOT NULL DEFAULT false,
    created_at      timestamptz NOT NULL DEFAULT now(),
    updated_at      timestamptz NOT NULL DEFAULT now(),
    CHECK (depository_inn ~ '^[0-9]{10}$'),
    UNIQUE (deponent_code, account_id, section_id)
);
COMMENT ON TABLE depo_accounts IS 'Счета депо инвестора и их разделы у различных депозитариев.';
COMMENT ON COLUMN depo_accounts.deponent_code IS 'Код депонента у конкретного депозитария (XSD SettlementDepositoryLocationType.DeponentCode).';
COMMENT ON COLUMN depo_accounts.account_id IS 'Номер счёта депо (XSD AccountIdType).';
COMMENT ON COLUMN depo_accounts.section_id IS 'Номер раздела счёта депо.';
COMMENT ON COLUMN depo_accounts.is_trading IS 'Признак торгового раздела (для отделения от изолированных).';

CREATE INDEX IF NOT EXISTS idx_depo_accounts_client ON depo_accounts(client_id);
CREATE INDEX IF NOT EXISTS idx_depo_accounts_deponent ON depo_accounts(deponent_code);

-- ---------------------------------------------------------------------
-- portfolios — портфели и остатки ЦБ
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS portfolios (
    id                    uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    client_id             uuid NOT NULL REFERENCES clients(id) ON DELETE CASCADE,
    depo_account_id       uuid NOT NULL REFERENCES depo_accounts(id) ON DELETE CASCADE,
    security_code         char(12) NOT NULL REFERENCES securities(security_code),
    isin                  char(12),
    quantity_whole        numeric(38, 0),
    quantity_fractional   numeric(38, 16),
    isolation_status      varchar(4) NOT NULL DEFAULT 'SGDN',
    valued_at             timestamptz NOT NULL,
    created_at            timestamptz NOT NULL DEFAULT now(),
    updated_at            timestamptz NOT NULL DEFAULT now(),
    CHECK (isolation_status IN ('SGDN')),
    CHECK ((quantity_whole IS NOT NULL) OR (quantity_fractional IS NOT NULL)),
    CHECK (isin IS NULL OR isin ~ '^[A-Z]{2}[A-Z0-9]{9}[0-9]$')
);
COMMENT ON TABLE portfolios IS 'Остатки ценных бумаг на счетах депо. Whole/Fractional — choice по XSD QuantityType (заполняется ровно одно).';
COMMENT ON COLUMN portfolios.quantity_whole IS 'Целое количество (для акций, облигаций).';
COMMENT ON COLUMN portfolios.quantity_fractional IS 'Дробное количество (для паёв ПИФ, до 16 знаков после точки).';
COMMENT ON COLUMN portfolios.isolation_status IS 'Статус обособления по XSD НРД, всегда SGDN.';
COMMENT ON COLUMN portfolios.valued_at IS 'Дата/время оценки (на какой момент актуален остаток).';

CREATE INDEX IF NOT EXISTS idx_portfolios_client ON portfolios(client_id);
CREATE INDEX IF NOT EXISTS idx_portfolios_depo ON portfolios(depo_account_id);
CREATE INDEX IF NOT EXISTS idx_portfolios_security ON portfolios(security_code);
CREATE INDEX IF NOT EXISTS idx_portfolios_valued_at ON portfolios(valued_at DESC);

-- ---------------------------------------------------------------------
-- etl_errors — ошибки выгрузки Fansy
-- ---------------------------------------------------------------------

CREATE TABLE IF NOT EXISTS etl_errors (
    id              bigserial PRIMARY KEY,
    source_table    text NOT NULL,
    source_pk       text,
    payload         jsonb,
    error_message   text NOT NULL,
    created_at      timestamptz NOT NULL DEFAULT now()
);
COMMENT ON TABLE etl_errors IS 'Журнал ошибок выгрузки Fansy: что не смогли записать в staging и почему.';
COMMENT ON COLUMN etl_errors.source_table IS 'Название таблицы в источнике (Fansy).';
COMMENT ON COLUMN etl_errors.source_pk IS 'Первичный ключ записи в источнике (для повторной попытки).';
COMMENT ON COLUMN etl_errors.payload IS 'Сама запись, которую не удалось загрузить (для диагностики).';

CREATE INDEX IF NOT EXISTS idx_etl_errors_created ON etl_errors(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_etl_errors_table ON etl_errors(source_table);
