-- 029_extend_matching_valuation_dynamics.sql
-- Purpose: Extend matching/valuation/dynamics tables per PR #445 deep-code-reviewer follow-ups.
--          Addresses 3 MEDIUM blockers:
--            A. house_sources   — add ext_url / raw_payload / last_seen_at (Stage 8 conflict resolution)
--            B. listing_sources — add price_rub / area_m2 / floor / rooms_count / raw_payload / last_seen_at
--            C. external_valuations — split sale/rent + chart_change_pct/dir + filters_hash + external_house_id
--               (Stage 7 Cian Valuation full save)
--            D. houses_price_dynamics — add room_count / prices_type / period dimensions + replace UNIQUE
--               (Stage 6 newbuilding multi-dim chart)
--            E. houses — add cadastral_number for Tier-0 Cian Valuation house matching
--
-- Dependencies (must already exist):
--   - house_sources       (028_matching_tables.sql)
--   - listing_sources     (028_matching_tables.sql)
--   - external_valuations (026_external_valuations.sql)
--   - houses_price_dynamics (024_houses_price_dynamics.sql)
--   - houses              (009_houses.sql)
--
-- Deploy order: Apply after 028.
-- Re-run safe: all ADD COLUMN IF NOT EXISTS; DO $$ blocks guard constraint changes.
--
-- NOTE on column naming: house_sources uses (ext_source, ext_id) per 028 DDL.
--                        listing_sources uses (ext_source, ext_id, source_url, last_scraped_at) per 028 DDL.
--                        We add complementary columns; existing column aliases documented inline.
--
-- Source refs:
--   Cross_Source_Matching_Strategy sec 2.1 (house_sources columns)
--   Cross_Source_Matching_Strategy sec 2.2 (listing_sources columns)
--   Schema_Cian_SERP_Inventory sec 26.3 (external_valuations full shape)
--   Schema_Cian_SERP_Inventory sec 16.3 (houses_price_dynamics multi-dim structure)

BEGIN;

-- =============================================================================
-- A. Extend house_sources
--    Add: ext_url, raw_payload, last_seen_at
--    (ext_source/ext_id/confidence/matched_method/matched_at already in 028)
-- =============================================================================

ALTER TABLE house_sources
  ADD COLUMN IF NOT EXISTS ext_url      text,            -- canonical URL to the source listing/page
  ADD COLUMN IF NOT EXISTS raw_payload  jsonb,           -- full source-side payload snapshot for diffing
  ADD COLUMN IF NOT EXISTS last_seen_at timestamptz;     -- last time this house was observed from this source

-- GIN index for raw_payload JSON querying (diff, field extraction)
CREATE INDEX IF NOT EXISTS house_sources_raw_payload_gin_idx
  ON house_sources USING GIN (raw_payload)
  WHERE raw_payload IS NOT NULL;

-- Recency index for stale-data detection per source
CREATE INDEX IF NOT EXISTS house_sources_last_seen_idx
  ON house_sources (last_seen_at DESC)
  WHERE last_seen_at IS NOT NULL;

-- =============================================================================
-- B. Extend listing_sources
--    Add: price_rub, area_m2, floor, rooms_count, raw_payload, last_seen_at
--    (ext_source/ext_id/confidence/matched_method/source_url/last_scraped_at already in 028)
--    Note: last_seen_at is a semantic alias for last_scraped_at — both kept for compat.
--          New code should write to last_seen_at; last_scraped_at preserved for existing queries.
-- =============================================================================

ALTER TABLE listing_sources
  ADD COLUMN IF NOT EXISTS price_rub    bigint,          -- price from this source at last scrape (cross-source divergence)
  ADD COLUMN IF NOT EXISTS area_m2      numeric(8,2),    -- area as seen on this source
  ADD COLUMN IF NOT EXISTS floor        int,             -- floor from this source
  ADD COLUMN IF NOT EXISTS rooms_count  int,             -- rooms count from this source
  ADD COLUMN IF NOT EXISTS raw_payload  jsonb,           -- full source-side payload snapshot
  ADD COLUMN IF NOT EXISTS last_seen_at timestamptz;     -- last time we saw this listing on source

-- GIN index for raw_payload JSON querying
CREATE INDEX IF NOT EXISTS listing_sources_raw_payload_gin_idx
  ON listing_sources USING GIN (raw_payload)
  WHERE raw_payload IS NOT NULL;

-- Recency index (complement existing listing_sources_scraped_idx on last_scraped_at)
CREATE INDEX IF NOT EXISTS listing_sources_last_seen_idx
  ON listing_sources (last_seen_at DESC)
  WHERE last_seen_at IS NOT NULL;

-- =============================================================================
-- C. Extend external_valuations
--    Add: sale_price_rub, sale_accuracy, rent_price_rub, rent_accuracy,
--         chart_change_pct, chart_change_direction, filters_hash, external_house_id
--    Existing: price_rub (primary estimate), accuracy (generic), chart, raw_payload
--    New columns provide sale/rent split and Cian-specific metadata.
-- =============================================================================

ALTER TABLE external_valuations
  -- Sale estimate (split from generic price_rub / accuracy)
  ADD COLUMN IF NOT EXISTS sale_price_rub         numeric,      -- sale price estimate
  ADD COLUMN IF NOT EXISTS sale_accuracy          numeric,      -- sale accuracy 0-100 (Cian-specific)
  -- Rent estimate (Cian only)
  ADD COLUMN IF NOT EXISTS rent_price_rub         numeric,      -- rent price per month estimate
  ADD COLUMN IF NOT EXISTS rent_accuracy          numeric,      -- rent accuracy 0-100
  -- Chart-level summary (from estimationChart)
  ADD COLUMN IF NOT EXISTS chart_change_pct       numeric(5,2), -- price change % since chart start
  ADD COLUMN IF NOT EXISTS chart_change_direction text,         -- 'increase' / 'decrease' / 'neutral'
  -- Cian internal metadata
  ADD COLUMN IF NOT EXISTS filters_hash           text,         -- sha256 of normalized filter params (dedup slight variations)
  ADD COLUMN IF NOT EXISTS external_house_id      bigint;       -- Cian's internal houseId (filters.houseId)

-- Index on filters_hash for dedup queries
CREATE INDEX IF NOT EXISTS ev_filters_hash_idx
  ON external_valuations (filters_hash)
  WHERE filters_hash IS NOT NULL;

-- Index on external_house_id for Cian house matching lookups
CREATE INDEX IF NOT EXISTS ev_external_house_id_idx
  ON external_valuations (external_house_id)
  WHERE external_house_id IS NOT NULL;

-- =============================================================================
-- D. Extend houses_price_dynamics
--    Add: room_count text, prices_type text, period text (dimension columns)
--    Replace UNIQUE(house_id, month_date, source) with multi-dim UNIQUE.
--
--    room_count uses Cian API enum text values:
--      'studio'/'oneRoom'/'twoRoom'/'threeRoom'/'fourPlusRooms' / 'all' (aggregate)
--    prices_type: 'price' (total) / 'priceSqm' (per sqm)
--    period: 'halfYear' / 'year' / 'allTime' — chart time window
-- =============================================================================

ALTER TABLE houses_price_dynamics
  ADD COLUMN IF NOT EXISTS room_count   text,   -- Cian room enum or 'all' for aggregate
  ADD COLUMN IF NOT EXISTS prices_type  text,   -- 'price' / 'priceSqm'
  ADD COLUMN IF NOT EXISTS period       text;   -- 'halfYear' / 'year' / 'allTime'

-- Backfill existing rows so they have non-NULL dimension values.
-- Historical rows had no dimension granularity; mark as aggregate/default.
UPDATE houses_price_dynamics
SET
  room_count   = COALESCE(room_count,  'all'),
  prices_type  = COALESCE(prices_type, 'priceSqm'),
  period       = COALESCE(period,      'allTime')
WHERE room_count IS NULL OR prices_type IS NULL OR period IS NULL;

-- Replace old 3-column UNIQUE with 6-column multi-dimension version.
-- Idempotent: checks pg_constraint by name before each action.
DO $$
BEGIN
    -- Drop old constraint if it still exists (default PG naming convention)
    IF EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'houses_price_dynamics_house_id_month_date_source_key'
          AND conrelid = 'houses_price_dynamics'::regclass
    ) THEN
        ALTER TABLE houses_price_dynamics
            DROP CONSTRAINT houses_price_dynamics_house_id_month_date_source_key;
    END IF;

    -- Add new constraint only if not already present (safe re-run)
    IF NOT EXISTS (
        SELECT 1 FROM pg_constraint
        WHERE conname = 'houses_price_dynamics_dim_key'
          AND conrelid = 'houses_price_dynamics'::regclass
    ) THEN
        ALTER TABLE houses_price_dynamics
            ADD CONSTRAINT houses_price_dynamics_dim_key
            UNIQUE (house_id, source, room_count, prices_type, period, month_date);
    END IF;
END
$$;

-- Supporting index for multi-dim read pattern (complements existing hpd_house_date_idx)
CREATE INDEX IF NOT EXISTS hpd_house_dim_idx
  ON houses_price_dynamics (house_id, source, room_count, prices_type, period, month_date DESC);

-- =============================================================================
-- E. Extend houses — add cadastral_number for Tier-0 house matching
--    Enables: SELECT id FROM houses WHERE cadastral_number = :ck  (confidence=1.0)
--    Sources: Cian offer.buildingCadastralNumber, cad_buildings.kadastr_num, DomRF KN
--    Format: '66:41:0000000:12345' (standard cadastral format)
-- =============================================================================

ALTER TABLE houses
  ADD COLUMN IF NOT EXISTS cadastral_number text;  -- building cadastral number

-- BTree index for exact Tier-0 match lookups
CREATE INDEX IF NOT EXISTS houses_cadastral_number_idx
  ON houses (cadastral_number)
  WHERE cadastral_number IS NOT NULL;

COMMIT;
