"""Scoring v2 — uses prod-style macro factors + per-flat metrics from Objective.

Components (all 0..100):
  education    20%   школы (×1) + садики (×1) + ВУЗы (×0.3) — distance score
  health       10%   аптеки (×1) + поликлиники (×1) + больницы (×0.5)
  retail       13%   max(big, 0.7×med) — distance score
  transit      15%   max(metro, 0.85×tram, 0.7×bus) — distance score
  leisure       9%   парки + площадки + спорт
  economic     30%   real_*: price_position 50% + velocity_real 25% + liquidity 25%
                     velocity adjusted by trend_factor (clamp 0.7..2.0, prod-style)
  market        3%   competitive density (jk_count_1km) + saturation factor
                     (informational; small weight)

Macro context shown alongside but not in score:
  mortgage_rate_sverdl, city_avg_poi_1km, city_med_price_m2.
"""
import sqlite3, pathlib, math
DB = pathlib.Path(__file__).parent / "analysis.db"

EDU = [("kindergarten", 300, 1000, 1.0),
       ("school",       400, 1500, 1.0),
       ("university",   1000, 5000, 0.3)]
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_M  = [("metro", 1000, 3000, 1.0)]
TRANSIT_T  = [("tram_stop", 400, 1500, 1.0)]
TRANSIT_B  = [("bus_stop",  200, 800,  1.0)]
LEISURE = [("park",       500, 2000, 1.0),
           ("playground", 200, 700,  1.0),
           ("sports",     500, 2000, 0.7)]


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 comp(conn, sid, cats):
    tw = sum(c[3] for c in cats); s = 0
    for cat, ideal, mx, w in cats:
        d = conn.execute("SELECT MIN(distance_m) FROM pois WHERE site_id=? AND category=?",
                         (sid, cat)).fetchone()[0]
        s += w * dist_score(d, ideal, mx)
    return s / tw if tw else 0


def hav(la1, lo1, la2, lo2):
    R = 6371000; p1, p2 = math.radians(la1), math.radians(la2)
    dp = math.radians(la2-la1); dl = math.radians(lo2-lo1)
    a = math.sin(dp/2)**2 + math.cos(p1)*math.cos(p2)*math.sin(dl/2)**2
    return 2*R*math.asin(math.sqrt(a))


def main():
    conn = sqlite3.connect(DB)
    weights = {r[0]: r[1] for r in conn.execute("SELECT component, weight FROM scoring_weights").fetchall()}
    if not weights:
        weights = {"education":0.18,"health":0.10,"retail":0.13,"transit":0.15,
                   "leisure":0.09,"economic":0.30,"market":0.05}
    print("Weights:", weights)

    sites = conn.execute("SELECT site_id, lat, lon FROM sites").fetchall()
    site_coords = {sid: (la, lo) for sid, la, lo in sites}

    # Bounds for normalization (computed once)
    prices = sorted([r[0] for r in conn.execute(
        "SELECT real_median_price_m2 FROM district_economics WHERE real_median_price_m2 IS NOT NULL").fetchall()])
    pmin, pmax = (prices[0], prices[-1]) if prices else (100, 200)
    if pmax <= pmin: pmax = pmin + 1

    vels = sorted([r[0] for r in conn.execute(
        "SELECT real_velocity_6mo FROM district_economics WHERE real_velocity_6mo IS NOT NULL").fetchall()])
    vmax = vels[int(len(vels) * 0.9)] if vels else 8

    # Citywide median jk_count_1km — for market component
    counts = sorted([r[0] for r in conn.execute(
        "SELECT count(*) FROM sites s2 JOIN sites s ON s2.site_id != s.site_id "
        "WHERE 1=0 GROUP BY s.site_id").fetchall()])  # noop, computed below

    conn.execute("DELETE FROM features")
    conn.execute("DELETE FROM scores")
    conn.execute("DELETE FROM scores_total")

    for sid, lat, lon in sites:
        # POI features
        for cat in ["kindergarten","school","university","pharmacy","clinic","hospital",
                    "shop_big","shop_med","shop_small","bus_stop","tram_stop","metro",
                    "park","playground","sports"]:
            n = conn.execute("SELECT MIN(distance_m) FROM pois WHERE site_id=? AND category=?",
                             (sid, cat)).fetchone()[0]
            c500 = conn.execute("SELECT count(*) FROM pois WHERE site_id=? AND category=? AND distance_m<=500",
                                (sid, cat)).fetchone()[0]
            c1k = conn.execute("SELECT count(*) FROM pois WHERE site_id=? AND category=? AND distance_m<=1000",
                               (sid, cat)).fetchone()[0]
            for k, v in (("nearest_m", n), ("count_500m", c500), ("count_1km", c1k)):
                conn.execute("INSERT INTO features(site_id,feature,value) VALUES (?,?,?)",
                             (sid, f"{cat}_{k}", float(v) if v is not None else None))

        # Locational components
        edu = comp(conn, sid, EDU)
        health = comp(conn, sid, HEALTH)
        retail = max(comp(conn, sid, RETAIL_BIG), 0.7 * comp(conn, sid, RETAIL_MED))
        transit = max(comp(conn, sid, TRANSIT_M),
                      0.85 * comp(conn, sid, TRANSIT_T),
                      0.7 * comp(conn, sid, TRANSIT_B))
        leisure = comp(conn, sid, LEISURE)

        # Economic + market
        econ_row = conn.execute("""SELECT de.real_median_price_m2, de.real_velocity_6mo,
                                          de.real_trend_ratio, de.months_to_sellout,
                                          de.real_sold_pct, de.real_n_lots
                                     FROM site_district sd
                                     JOIN district_economics de USING (district)
                                    WHERE sd.site_id=?""", (sid,)).fetchone()
        if econ_row:
            price, v_rec, trend, mts, sold_pct, n_lots = econ_row
            # price_score: linear by district median price percentile
            p_score = max(0, min(100, ((price or 0) - pmin) * 100 / (pmax - pmin)))
            # velocity_score: real recent (6mo), capped at p90
            v_score = max(0, min(100, (v_rec or 0) * 100 / vmax)) if vmax else 0
            # trend modifier (prod-style): clamp 0.7..2.0 → 0.5..1.0 multiplier on velocity
            tf = max(0.7, min(2.0, trend or 1.0))
            v_score = v_score * (0.5 + 0.5 * (tf / 2.0))  # 0.7→0.675, 1.0→0.75, 2.0→1.0
            # liquidity_score from months_to_sellout
            liq_score = max(0, 100 - min(mts or 24, 24) * 100 / 24) if mts else 50
            economic = 0.50 * p_score + 0.25 * v_score + 0.25 * liq_score
        else:
            economic = 0; trend = None; sold_pct = None; n_lots = None

        # Competitive density: number of OTHER ЖК within 1km
        n_jk_1km = sum(
            1 for sid2, (la2, lo2) in site_coords.items()
            if sid2 != sid and sid2.startswith("jk:") and hav(lat, lon, la2, lo2) <= 1000
        )
        # market component: 50/50 saturation × density penalty
        # density: 0..15 jks → 100..0 (linearly capped)
        density_score = max(0, 100 - n_jk_1km * 100 / 15)
        # saturation: optimal 50% sold (proven absorption, room to grow). 0% raw, 100% over-saturated
        sat_score = 100 - abs((sold_pct or 50) - 50) * 2 if sold_pct is not None else 50
        market = 0.5 * density_score + 0.5 * sat_score
        conn.execute("INSERT INTO features(site_id,feature,value) VALUES (?,?,?)",
                     (sid, "jk_count_1km", n_jk_1km))

        comps = {"education": edu, "health": health, "retail": retail,
                 "transit": transit, "leisure": leisure, "economic": economic,
                 "market": market}
        for c, val in comps.items():
            conn.execute("INSERT INTO scores(site_id,component,score_0_100) VALUES (?,?,?)",
                         (sid, c, val))
        weighted = sum(weights.get(k, 0) * v for k, v in comps.items())
        conn.execute("INSERT INTO scores_total(site_id,weighted) VALUES (?,?)", (sid, weighted))

    conn.commit()

    # Ranks
    rows = conn.execute("""SELECT s.site_id, st.weighted FROM sites s
                            JOIN scores_total st USING (site_id)
                            ORDER BY st.weighted DESC""").fetchall()
    for rank, (sid, _) in enumerate(rows, 1):
        conn.execute("UPDATE scores_total SET rank_overall=? WHERE site_id=?", (rank, sid))
    # district rank
    by_dist = {}
    for sid, _ in rows:
        d = conn.execute("SELECT district FROM site_district WHERE site_id=?", (sid,)).fetchone()
        by_dist.setdefault(d[0] if d else "—", []).append(sid)
    for d, 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()

    # Print parcel summary
    pid = "parcel:66:41:0204016:10"
    p = conn.execute("SELECT weighted, rank_overall, rank_district FROM scores_total WHERE site_id=?",
                     (pid,)).fetchone()
    print(f"\n=== Parcel ===")
    print(f"  total: {p[0]:.2f}/100   overall #{p[1]}   district #{p[2]}")
    print(f"  components:")
    for r in conn.execute("SELECT component, score_0_100 FROM scores WHERE site_id=? ORDER BY component", (pid,)).fetchall():
        wt = weights.get(r[0], 0)
        print(f"    {r[0]:<10} {r[1]:>6.1f} × {wt*100:>4.0f}% = {r[1]*wt:>5.2f}")
    n_jk = conn.execute("SELECT count(*) FROM sites WHERE kind='jk'").fetchone()[0]
    print(f"  vs {n_jk} строящихся ЖК Ekb")
    conn.close()

if __name__ == "__main__":
    main()
