-- 86_v_bucket_success_score.sql
-- Context: Success-driven bucket ranking view for recommend_mix (issue #25).
-- Per-(district × class × room_bucket) aggregates from domrf_kn_flats + domrf_kn_objects
-- over the last 24 months, with Z-scores normalised per (district, class) group.
-- Used in recommend_mix to bias recommendations toward buckets with high velocity + price.
--
-- Dependencies:
--   domrf_kn_flats   — total_area, rooms, price_per_m2, obj_id, region_cd, snapshot_date
--   domrf_kn_objects — obj_id, district_name, obj_class, region_cd, snapshot_date
--   domrf_kn_sale_graph — obj_id, report_month, contracted, snapshot_date
--
-- Deploy order: standalone, no prior migration needed.
-- Idempotent: CREATE OR REPLACE VIEW.

BEGIN;

CREATE OR REPLACE VIEW v_bucket_success_score AS
WITH
-- Latest snapshot per object (both tables are time-series)
latest_obj AS (
    SELECT DISTINCT ON (obj_id)
        obj_id, district_name, obj_class, region_cd
    FROM domrf_kn_objects
    WHERE district_name IS NOT NULL
    ORDER BY obj_id, snapshot_date DESC
),

bucket_aggs AS (
    SELECT
        o.district_name,
        COALESCE(o.obj_class, 'Comfort') AS obj_class,
        CASE
            WHEN f.rooms = 0 OR f.total_area < 30 THEN 'Студии 15-30'
            WHEN f.total_area < 45              THEN '1-к 30-45'
            WHEN f.total_area < 60              THEN '2-к 45-60'
            WHEN f.total_area < 80              THEN '3-к 60-80'
            ELSE '80+ м²'
        END AS bucket,
        AVG(NULLIF(f.price_per_m2, 0))   AS avg_price_per_m2,
        AVG(NULLIF(f.total_area,   0))   AS avg_area_m2,
        COUNT(*)                         AS n_deals,
        -- Velocity proxy: avg monthly contracted units per object, over last 24 months
        AVG(
            (SELECT AVG(sg.contracted)
               FROM domrf_kn_sale_graph sg
              WHERE sg.obj_id = o.obj_id
                AND sg.report_month > NOW() - INTERVAL '24 months')
        ) AS avg_velocity_per_month
    FROM domrf_kn_flats f
    JOIN latest_obj o ON o.obj_id = f.obj_id
    WHERE f.total_area BETWEEN 15 AND 200
      AND f.price_per_m2 BETWEEN 30000 AND 500000
      AND o.region_cd = 66
      AND f.snapshot_date > CURRENT_DATE - INTERVAL '24 months'
    GROUP BY 1, 2, 3
    HAVING COUNT(*) >= 15  -- мин 15: weak-confidence (15-29), strong (≥30)
),

z_scores AS (
    SELECT
        district_name, obj_class, bucket,
        n_deals, avg_price_per_m2, avg_area_m2, avg_velocity_per_month,
        -- Z-score нормированный по группе (district × class)
        (avg_velocity_per_month
            - AVG(avg_velocity_per_month) OVER (PARTITION BY district_name, obj_class))
            / NULLIF(STDDEV(avg_velocity_per_month) OVER (PARTITION BY district_name, obj_class), 0)
            AS velocity_z,
        (avg_price_per_m2
            - AVG(avg_price_per_m2) OVER (PARTITION BY district_name, obj_class))
            / NULLIF(STDDEV(avg_price_per_m2) OVER (PARTITION BY district_name, obj_class), 0)
            AS price_z,
        (avg_area_m2
            - AVG(avg_area_m2) OVER (PARTITION BY district_name, obj_class))
            / NULLIF(STDDEV(avg_area_m2) OVER (PARTITION BY district_name, obj_class), 0)
            AS area_z
    FROM bucket_aggs
)

SELECT
    district_name,
    obj_class,
    bucket,
    n_deals,
    avg_price_per_m2,
    avg_area_m2,
    avg_velocity_per_month,
    COALESCE(velocity_z, 0) * 0.5
        + COALESCE(price_z,    0) * 0.3
        - COALESCE(area_z,     0) * 0.2  AS success_score,
    COALESCE(velocity_z, 0)              AS velocity_z,
    COALESCE(price_z,    0)              AS price_z,
    COALESCE(area_z,     0)              AS area_z
FROM z_scores
ORDER BY district_name, obj_class, success_score DESC;

COMMENT ON VIEW v_bucket_success_score IS
    'Success-driven bucket ranking per (district, class). '
    'Z-scores: velocity (+) приоритет, price (+) приоритет, area (-) приоритет. '
    'Min 15 сделок в группе (15-29 = weak confidence, ≥30 = strong). '
    'Используется recommend_mix для смещения рекомендации (issue #25).';

COMMIT;
