"""Compute features and weighted parcel score.

Per-site feature vector:
  edu_kindergarten_nearest_m, edu_school_nearest_m, edu_university_nearest_m
  health_pharmacy_nearest_m, health_clinic_nearest_m, health_hospital_nearest_m
  retail_big_nearest_m, retail_med_nearest_m, retail_small_nearest_m
  transit_bus_nearest_m, transit_tram_nearest_m, transit_metro_nearest_m
  leisure_park_nearest_m, leisure_playground_nearest_m, leisure_sports_nearest_m
  + counts within 500m / 1000m for each category

Component scores 0..100 (higher = better):
  education = avg(score(kindergarten,500m), score(school,800m))
  health    = avg(score(pharmacy,500m), score(clinic,1000m))
  retail    = max(score(shop_big,1000m), 0.7*score(shop_med,500m))
  transit   = max(score(metro,1500m), 0.8*score(tram,500m), 0.6*score(bus,300m))
  leisure   = avg(score(park,800m), score(playground,400m), score(sports,800m))

Distance-to-score: piecewise linear,
  0..ideal_m  → 100
  ideal_m..max_m → 100..0
  >max_m      → 0

Total weighted (sums to 1.0):
  education 0.30   (садики/школы — главное для семейных, ядро ЦА девелопера)
  health    0.15
  retail    0.20
  transit   0.20
  leisure   0.15
"""
import sqlite3, pathlib

DB = pathlib.Path(__file__).parent / "analysis.db"

# (category, ideal_m, max_m, weight_in_component)
EDU = [("kindergarten", 300, 1000, 1.0),
       ("school",       400, 1500, 1.0),
       ("university",   1000, 5000, 0.3)]   # nice-to-have
HEALTH = [("pharmacy", 300, 1000, 1.0),
          ("clinic",   500, 2000, 1.0),
          ("hospital", 1500, 5000, 0.5)]
RETAIL_BIG = [("shop_big",  500, 2000, 1.0)]
RETAIL_MED = [("shop_med",  300, 1000, 1.0)]
TRANSIT_MAIN = [("metro", 1000, 3000, 1.0)]
TRANSIT_TRAM = [("tram_stop", 400, 1500, 1.0)]
TRANSIT_BUS  = [("bus_stop",  200, 800,  1.0)]
LEISURE = [("park",       500, 2000, 1.0),
           ("playground", 200, 700,  1.0),
           ("sports",     500, 2000, 0.7)]

WEIGHTS = {
    "education": 0.20,
    "health":    0.10,
    "retail":    0.15,
    "transit":   0.15,
    "leisure":   0.10,
    "economic":  0.30,
}

# Economic sub-weights (sum to 1, used inside `economic` component)
ECON_WEIGHTS = {"price": 0.50, "velocity": 0.25, "liquidity": 0.25}

def dist_score(d_m, ideal, mx):
    if d_m is None: return 0.0
    if d_m <= ideal: return 100.0
    if d_m >= mx: return 0.0
    return 100.0 * (mx - d_m) / (mx - ideal)

def component_score(conn, site_id, cats):
    """Average distance-score across the categories listed (with weights)."""
    total_w = sum(c[3] for c in cats)
    s = 0.0
    for cat, ideal, mx, w in cats:
        nearest = conn.execute(
            "SELECT MIN(distance_m) FROM pois WHERE site_id=? AND category=?",
            (site_id, cat)
        ).fetchone()[0]
        s += w * dist_score(nearest, ideal, mx)
    return s / total_w if total_w else 0.0

def features(conn, site_id):
    """Materialize feature vector — nearest distance + counts within 500/1000m."""
    feats = {}
    cats = ["kindergarten","school","university","pharmacy","clinic","hospital",
            "shop_big","shop_med","shop_small","bus_stop","tram_stop","metro",
            "park","playground","sports"]
    for cat in cats:
        nearest = conn.execute(
            "SELECT MIN(distance_m) FROM pois WHERE site_id=? AND category=?",
            (site_id, cat)).fetchone()[0]
        c500 = conn.execute(
            "SELECT count(*) FROM pois WHERE site_id=? AND category=? AND distance_m<=500",
            (site_id, cat)).fetchone()[0]
        c1000 = conn.execute(
            "SELECT count(*) FROM pois WHERE site_id=? AND category=? AND distance_m<=1000",
            (site_id, cat)).fetchone()[0]
        feats[f"{cat}_nearest_m"] = nearest
        feats[f"{cat}_count_500m"] = c500
        feats[f"{cat}_count_1km"] = c1000
    return feats

def economic_score(conn, site_id, econ_bounds):
    """Returns (component_0_100, breakdown dict) using REAL per-flat district aggregates.

    Prefers real_* columns from objective_lots (Поквартирные/Лоты, 303k lots);
    falls back to weighted_price_m2 + deals_per_month_avg from corp_sum if missing.
    """
    row = conn.execute("""SELECT
            COALESCE(de.real_median_price_m2, de.weighted_price_m2) AS price,
            COALESCE(de.real_velocity_per_month, de.deals_per_month_avg) AS v,
            de.months_to_sellout, de.real_sold_pct
            FROM site_district sd
            JOIN district_economics de USING (district)
           WHERE sd.site_id=?""", (site_id,)).fetchone()
    if not row:
        return 0.0, {}
    price, v, mts, sold_pct = row
    pmin, pmax, vmax, mts_cap = econ_bounds
    p_score = max(0.0, min(100.0, (price - pmin) * 100.0 / (pmax - pmin))) if price else 0.0
    v_score = max(0.0, min(100.0, (v or 0) * 100.0 / vmax))
    if mts is None:
        liq_score = 0.0
    else:
        liq_score = max(0.0, 100.0 - min(mts, mts_cap) * 100.0 / mts_cap)
    total = (ECON_WEIGHTS["price"] * p_score
             + ECON_WEIGHTS["velocity"] * v_score
             + ECON_WEIGHTS["liquidity"] * liq_score)
    return total, {"price_score": p_score, "velocity_score": v_score,
                   "liquidity_score": liq_score, "median_price_m2": price,
                   "velocity": v, "months_to_sellout": mts,
                   "district_sold_pct": sold_pct}


def main():
    conn = sqlite3.connect(DB)
    sites = conn.execute("SELECT site_id, kind FROM sites").fetchall()
    conn.execute("DELETE FROM features")
    conn.execute("DELETE FROM scores")
    conn.execute("DELETE FROM scores_total")

    # Compute econ bounds from district_economics — prefer real_*
    prices = sorted([r[0] for r in conn.execute(
        "SELECT COALESCE(real_median_price_m2, weighted_price_m2) FROM district_economics "
        "WHERE COALESCE(real_median_price_m2, weighted_price_m2) IS NOT NULL").fetchall()])
    if not prices: prices = [100, 200]
    pmin, pmax = prices[0], prices[-1]
    if pmax <= pmin: pmax = pmin + 1
    vels = sorted([r[0] for r in conn.execute(
        "SELECT COALESCE(real_velocity_per_month, deals_per_month_avg) FROM district_economics "
        "WHERE COALESCE(real_velocity_per_month, deals_per_month_avg) IS NOT NULL").fetchall()])
    vmax = vels[int(len(vels) * 0.9)] if vels else 1.0
    if vmax <= 0: vmax = 1.0
    econ_bounds = (pmin, pmax, vmax, 24.0)
    print(f"Econ bounds (real): price [{pmin:.1f}, {pmax:.1f}] тыс, vel_p90={vmax:.2f}, mts_cap=24 мес")

    for site_id, kind in sites:
        # features
        for k, v in features(conn, site_id).items():
            conn.execute("INSERT INTO features(site_id,feature,value) VALUES (?,?,?)",
                         (site_id, k, float(v) if v is not None else None))

        # component scores
        edu     = component_score(conn, site_id, EDU)
        health  = component_score(conn, site_id, HEALTH)
        # retail = max(big, 0.7*med). med alone is enough for daily needs.
        big = component_score(conn, site_id, RETAIL_BIG)
        med = component_score(conn, site_id, RETAIL_MED)
        retail = max(big, 0.7 * med)
        # transit: best of metro / tram / bus
        metro = component_score(conn, site_id, TRANSIT_MAIN)
        tram  = component_score(conn, site_id, TRANSIT_TRAM)
        bus   = component_score(conn, site_id, TRANSIT_BUS)
        transit = max(metro, 0.85 * tram, 0.7 * bus)
        leisure = component_score(conn, site_id, LEISURE)

        econ, _ = economic_score(conn, site_id, econ_bounds)
        comps = {"education": edu, "health": health, "retail": retail,
                 "transit": transit, "leisure": leisure, "economic": econ}
        for c, v in comps.items():
            conn.execute("INSERT INTO scores(site_id,component,score_0_100) VALUES (?,?,?)",
                         (site_id, c, v))

        weighted = sum(WEIGHTS[c] * v for c, v in comps.items())
        conn.execute("INSERT INTO scores_total(site_id,weighted) VALUES (?,?)",
                     (site_id, weighted))

    conn.commit()

    # Compute ranks
    rows = conn.execute("""SELECT s.site_id, s.kind, s.district, st.weighted
                           FROM sites s JOIN scores_total st USING (site_id)
                           ORDER BY st.weighted DESC""").fetchall()
    for rank, (sid, kind, _, _) in enumerate(rows, 1):
        conn.execute("UPDATE scores_total SET rank_overall=? WHERE site_id=?", (rank, sid))
    # district rank
    by_dist = {}
    for sid, kind, dist, _ in rows:
        by_dist.setdefault(dist or "—", []).append(sid)
    for dist, sids in by_dist.items():
        for rank, sid in enumerate(sids, 1):
            conn.execute("UPDATE scores_total SET rank_district=? WHERE site_id=?", (rank, sid))
    conn.commit()

    # Summary
    parcel_id = "parcel:66:41:0204016:10"
    p = conn.execute("SELECT weighted, rank_overall FROM scores_total WHERE site_id=?",
                     (parcel_id,)).fetchone()
    n = conn.execute("SELECT count(*) FROM sites").fetchone()[0]
    print(f"\n=== Parcel score ===")
    print(f"  weighted: {p[0]:.1f}/100   rank: {p[1]}/{n}")
    print("\n  Components:")
    for c, v in conn.execute("SELECT component, score_0_100 FROM scores WHERE site_id=?",
                             (parcel_id,)).fetchall():
        print(f"    {c:<10} {v:.1f}")
    print("\n  Nearest POIs (m):")
    for k, v in conn.execute(
        "SELECT feature, value FROM features WHERE site_id=? AND feature LIKE '%_nearest_m'",
        (parcel_id,)).fetchall():
        print(f"    {k:<35} {('%.0f m'%v) if v is not None else '—'}")

    print("\n=== Top-5 ЖК in Ekb ===")
    for r in conn.execute("""SELECT s.name, s.district, st.weighted, st.rank_overall
                             FROM sites s JOIN scores_total st USING (site_id)
                             WHERE s.kind='jk' ORDER BY st.weighted DESC LIMIT 5""").fetchall():
        print(f"  #{r[3]:>3}  {r[2]:>5.1f}  [{(r[1] or '—'):<25}] {r[0]}")

    conn.close()

if __name__ == "__main__":
    main()
