-- 144_gar_canon_addr_match.sql
--
-- CONTEXT: матч ГАР↔houses (мигр. 143) по ТОЧНОМУ равенству нормализованного адреса
--   (tradein_normalize_short_addr) на проде даёт 0 совпадений: нормализатор почти ничего
--   не канонизирует, поэтому houses-адрес «ул. Шаумяна,20» НИКОГДА не равен ГАР-строке
--   «ул. Шаумяна, 20» (тип улицы «ул.» vs «ул», запятая/пробел, точка). Знаменатель
--   «доля квартир дома в продаже» (v_building_sale_share) оставался пустым.
--
-- WHAT: канонический ключ адреса tradein_canon_addr(text) — агрессивно схлопывает:
--     1. lower() + ё→е;
--     2. срезает префикс района «р-н ...,»;
--     3. удаляет типы улиц (улица/ул/переулок/пер/проспект/.../микрорайон/мкр), но ТОЛЬКО
--        как самостоятельные токены на границе не-кириллических символов — буква в составе
--        названия («ПРоспект» внутри слова) не трогается;
--     4. оставляет только кириллицу-нижний-регистр + цифры (выбрасывает пробелы, точки,
--        запятые, дефисы, слэши).
--   Так «ул. Шаумяна,20» и «ул. Шаумяна, 20» → оба «шаумяна20» (совпадают),
--   «р-н Ленинский, улица Цвиллинга, 7/6» → «цвиллинга76». А «6Б» НЕ схлопывается до «6»
--   (буква корпуса остаётся кириллицей → другое здание).
--
--   Эмпирически на live-данных (ГАР region 66, 68 406 строк с flat_count>0) канон-ключ
--   поднимает матч с 0 до 2 769 из 6 808 вторичных домов (~41%, при ЕКБ-ограничении).
--   ЕКБ-ограничение (full_address ILIKE '%Екатеринбург%') обязательно: без него
--   «Машиностроителей 6» существует в 4 населённых пунктах region 66 → ложные коллизии.
--   Сам ре-матч (app/services/gar_flats_loader.match_houses_to_gar) — отдельный шаг после
--   деплоя (CLI `python -m app.tasks.gar_flats_load --match-only`); этот файл — только
--   функция + функциональный индекс под её JOIN.
--
-- DEPENDENCIES (existing prod objects):
--   - gar_house_flats (norm_address, full_address, flat_count, house_guid) — мигр. 143.
--   - houses.gar_* provenance-колонки — мигр. 143.
--
-- SAFETY / IDEMPOTENCY:
--   - CREATE OR REPLACE FUNCTION + CREATE INDEX IF NOT EXISTS → повторный прогон no-op.
--   - Функция IMMUTABLE (обязательно для функционального индекса).
--   - Раннер деплоя (deploy-tradein.yml) гонит файл через psql ON_ERROR_STOP=on БЕЗ
--     --single-transaction → транзакцию открывает сам файл (BEGIN/COMMIT ниже), как 143.
--
-- NB по нумерации: на forgejo/main последний — 143_building_sale_share_schema.sql
--   (139–142 заняты), следующий свободный sequential = 144 (проверено `ls | grep '^14'`).
--
-- Deploy order: после 143_building_sale_share_schema.sql.

BEGIN;

-- Канонический ключ адреса для устойчивого ГАР↔houses матча (см. шапку файла).
CREATE OR REPLACE FUNCTION tradein_canon_addr(s text) RETURNS text AS $func$
  SELECT regexp_replace(
    regexp_replace(
      regexp_replace(
        translate(lower(coalesce(s, '')), 'ё', 'е'),
        'р-н[^,]*,', '', 'g'),
      '(^|[^а-я])(улица|ул|переулок|пер|проспект|пркт|пр|бульвар|бр|шоссе|ш|проезд|набережная|наб|площадь|пл|тупик|туп|аллея|микрорайон|мкр)([^а-я]|$)',
      '\1\3', 'g'),
    '[^а-я0-9]', '', 'g');
$func$ LANGUAGE sql IMMUTABLE;

COMMENT ON FUNCTION tradein_canon_addr(text) IS
  'Канонический ключ адреса для ГАР↔houses матча (мигр. 144): lower+ё→е, срез «р-н ...,», '
  'удаление типов улиц как токенов на границе не-кириллицы, оставляет только [а-я0-9]. '
  'Поднял матч с 0 до ~41%% (2769/6808 вторички, ЕКБ-restricted). См. gar-flats-runbook.md.';

-- Функциональный индекс под JOIN gar_pick (canon = tradein_canon_addr(norm_address)),
-- partial по flat_count>0 — матчим только дома с известным знаменателем.
CREATE INDEX IF NOT EXISTS gar_house_flats_canon_idx
    ON gar_house_flats (tradein_canon_addr(norm_address))
    WHERE flat_count > 0;

COMMIT;
