-- 116_obj2_multi_feature_matcher.sql
-- Issue #307 OBJ-2: backfill objective_complex_mapping via multi-feature scoring
-- (name_sim 60% + dev_match 25% + district_match 15%).
--
-- Adds trgm gist index on objective_lots.project_name FIRST so the LATERAL
-- similarity() probe runs as bitmap index scan instead of seq-scan-per-row.
--
-- Inserts auto-accepted matches at composite >= 0.75 with match_method='multi_feature_v1'.
-- ON CONFLICT (objective_complex_name, objective_group) DO NOTHING — idempotent.
-- REFRESH mv_layout_velocity CONCURRENTLY at the end so Site Finder sees new mappings.
--
-- Verification (run after COMMIT, outside migration):
--   SELECT COUNT(*) FROM objective_complex_mapping;                       -- expect 200+
--   SELECT * FROM objective_complex_mapping WHERE domrf_obj_id=64701;     -- Малевич still mapped
--   SELECT match_method, COUNT(*) FROM objective_complex_mapping GROUP BY 1;

-- The pg_trgm extension is already installed (used by existing migrations 99/103/104).

-- ── Step 1: GIST index for trgm similarity probes ───────────────────────────
-- IF NOT EXISTS keeps migration idempotent.
CREATE INDEX IF NOT EXISTS objective_lots_project_name_trgm_idx
    ON objective_lots USING gist (project_name gist_trgm_ops);

-- ── Step 2: composite-score backfill ────────────────────────────────────────
BEGIN;

WITH unmapped_domrf AS (
    SELECT o.obj_id, o.comm_name, o.dev_name, o.district_name
      FROM domrf_kn_objects o
     WHERE o.is_ekb = true
       AND o.comm_name IS NOT NULL
       AND NOT EXISTS (
           SELECT 1 FROM objective_complex_mapping m WHERE m.domrf_obj_id = o.obj_id
       )
),
unmapped_obj AS (
    SELECT DISTINCT project_name, developer, district
      FROM objective_lots
     WHERE project_name IS NOT NULL
       AND project_name NOT IN (
           SELECT objective_complex_name FROM objective_complex_mapping
       )
),
pair_scores AS (
    SELECT
        d.obj_id,
        o.project_name,
        similarity(d.comm_name, o.project_name)                              AS name_sim,
        (regexp_replace(lower(coalesce(d.dev_name, '')),  '[^а-яa-z0-9]', '', 'g')
         = regexp_replace(lower(coalesce(o.developer, '')), '[^а-яa-z0-9]', '', 'g')
        )::int                                                                AS dev_match,
        (
            coalesce(d.district_name, '') ILIKE '%' || coalesce(o.district, '') || '%'
         OR coalesce(o.district, '')      ILIKE '%' || coalesce(d.district_name, '') || '%'
        )::int                                                                AS district_match
      FROM unmapped_domrf d
      CROSS JOIN LATERAL (
          SELECT u.project_name, u.developer, u.district
            FROM unmapped_obj u
           WHERE similarity(d.comm_name, u.project_name) > 0.3
           ORDER BY similarity(d.comm_name, u.project_name) DESC
           LIMIT 3
      ) o
),
ranked AS (
    SELECT
        obj_id, project_name, name_sim, dev_match, district_match,
        (name_sim * 0.6 + dev_match * 0.25 + district_match * 0.15)         AS composite,
        ROW_NUMBER() OVER (
            PARTITION BY obj_id
            ORDER BY (name_sim * 0.6 + dev_match * 0.25 + district_match * 0.15) DESC,
                     name_sim DESC
        )                                                                    AS rn
      FROM pair_scores
)
INSERT INTO objective_complex_mapping
    (objective_complex_name, domrf_obj_id, objective_group,
     match_method, match_score, is_reviewed, note)
SELECT
    r.project_name,
    r.obj_id,
    'Екатеринбург',
    'multi_feature_v1',
    r.composite,
    false,
    'OBJ-2 multi-feature 2026-05-17 (name 0.6 + dev 0.25 + district 0.15)'
  FROM ranked r
 WHERE r.rn = 1
   AND r.composite >= 0.75
ON CONFLICT (objective_complex_name, objective_group) DO NOTHING;

COMMIT;

-- ── Step 3: REFRESH MV so Site Finder uses new mappings ─────────────────────
-- CONCURRENTLY requires the MV to not be in a tx; run outside BEGIN/COMMIT.
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_layout_velocity;
