from pathlib import Path
import json,re
import pandas as pd
R=Path(__file__).resolve().parent
v=pd.read_parquet(R/'reviews.parquet');v['created']=pd.to_datetime(v.timestamp_created,unit='s',utc=True)
t=pd.read_parquet(R/'targets.parquet').set_index('appid')
E={
'93920127':('a327ex','stated_cross_genre_purchase','Asks for an autochess-like tower-defense game by a327ex and explicitly says they would buy it; also requests information and more configuration options. Stated intent, not an observed purchase.'),
'95224877':('a327ex','recognized_common_configuration','Recognizes BYTEPATH and SNKRX as small games combining mechanics and varied configurations, and recommends watching the developer for future work.'),
'95034038':('a327ex','cross_catalog_build_pleasure','Enjoys SNKRX and details actual BYTEPATH builds, including homing projectiles and screen-wide explosions; accepts roughness while valuing build possibilities.'),
'116097298':('a327ex','identity_discovered_after_both','Says they liked both games before discovering the shared developer credit; then identifies as a fan. Recognition follows consumption in this account.'),
'158054318':('a327ex','future_interest_with_conditions','Likes BYTEPATH too and looks forward to future work, while distinguishing SNKRX onboarding and criticizing performance.'),
'213631890':('a327ex','returning_player_interface_friction','Recognizes SNKRX and expects its mouse-control option; wants to like BYTEPATH but objects to interface and sensory effects.'),
'92351640':('a327ex','build_core_named','Describes the appeal as a build simulator attached to a simple game.'),
'41403451':('a327ex','discovery_of_combinations','Describes arriving from Path of Exile discussion and finding desired build discovery in BYTEPATH; values the menus and theorycrafting.'),
'170292577':('Rad Codex','same_build_family_different_preferences','Loved Horizon\'s Gate but rejects Kingsvein\'s greater emphasis on pushing, terrain and combo combat; prefers the previous range of classes and roleplaying options.'),
'209710833':('Rad Codex','recognizable_class_system_and_reverse_discovery','Recognizes hybrid class systems across the games; says Kingsvein was their first and that they are now playing earlier work.'),
'134700551':('Rad Codex','small_departure_positive_but_qualified','Likes Azalea despite finding its puzzles less interesting than other Rad Codex games; expresses general affection for the developer.'),
'122461008':('Rad Codex','small_departure_and_prior_rpgs','Enjoys the short adventure and explicitly recommends the developer\'s Voidspire and Alvora RPGs.'),
'221865165':('Desert Fox','familiarity_does_not_ensure_approval','Says they love the developer\'s games but reject Full Moon\'s slow walking sections while enjoying the point-and-click chapters.'),
'192207128':('Desert Fox','stable_bundle_and_stated_repeat_purchase','Values the repeated combination of dream logic, mood, vague storytelling and alternate routes; says they will keep buying this kind of work.'),
'157587906':('Desert Fox','recognizable_experience','Connects puzzles, creepy art, story and restrained presentation to earlier titles; expresses intent to keep buying.'),
'145812937':('Zachtronics','prior_puzzles_open_door','Says love for the puzzle games motivated trying Eliza, a form they do not usually play, and reports enjoying it.'),
'54860514':('Zachtronics','theme_continuity','Recognizes technology/development themes persisting through the new narrative form.'),
'54592158':('Zachtronics','authorship_beyond_mechanics','Attributes the studio\'s appeal to subject matter and its treatment, alongside puzzles and presentation; finds that continuity in Eliza.'),
'54604246':('Zachtronics','trust_buys_trial_not_unqualified_praise','Says prior regard caused a purchase without reviews or prior game knowledge; gives a qualified positive review and a 6/10 judgment.'),
'190599746':('Zachtronics','prior_fan_disappointed','Enjoyed prior games and knowingly tried a visual novel, but does not recommend this one.'),
'54688217':('Zachtronics','story_affinity_not_sufficient','Liked story elements in prior puzzle games but dislikes Eliza\'s writing and dramatic development.')}
rows=[]
for rid,(dev,theme,note) in E.items():
 a=v[v.recommendationid.astype(str).eq(rid)].iloc[0]
 rows.append(dict(review_id=rid,appid=int(a.appid),game=a.game,developer=dev,theme=theme,note=note,voted_up=bool(a.voted_up),created=str(a.created),text=a.review,
                  steam_source=f'https://store.steampowered.com/appreviews/{a.appid}?json=1',record_locator=f'reviews.parquet recommendationid={rid}'))
pd.DataFrame(rows).to_csv(R/'authorship_evidence.csv',index=False)

windows=[]
for appid in [760330,915310]:
 a=t.loc[appid];z=v[v.appid.eq(appid)];d=a.first_date
 for day in [1,7,14,30,90,365]:windows.append(dict(game=a['name'],days=day,reviews=int(z.created.lt(d+pd.Timedelta(days=day)).sum())))
pd.DataFrame(windows).to_csv(R/'own_game_launch_windows.csv',index=False)

p=pd.read_csv(R/'historical_pairs.csv')
changes=p[p.predecessor_band_at_launch.ne(p.predecessor_band_now)]
summary=dict(pairs=len(p),band_changes=len(changes),band_change_examples=changes[['developer','name','previous_reviews_at_launch','previous_reviews_now']].to_dict('records'))
(R/'historical_bias_summary.json').write_text(json.dumps(summary,indent=2))

seqs=[('Rad Codex',1224290,1366100,2276830),('Studio Fizbin',1191900,1278750,2129810),('Tuatara Games',505630,1186660,2050800),('Desert Fox',538070,2540460,3202410)]
h=pd.read_csv(R/'game_history.csv').set_index('appid');sequences=[]
for dev,anchor,quiet,new in seqs:
 a,b,c=[h.loc[i] for i in [anchor,quiet,new]]
 cutoff=t.loc[new].first_date
 sequences.append(dict(developer=dev,anchor=a['name'],quiet=b['name'],return_game=c['name'],anchor_current=int(a.n),quiet_current=int(b.n),return_current=int(c.n),
 anchor_day30=int(a.count_by_day30),quiet_day30=int(b.count_by_day30),return_day30=int(c.count_by_day30),
 anchor_day90=int(a.count_by_day90),quiet_day90=int(b.count_by_day90),return_day90=int(c.count_by_day90),
 current_return_to_anchor=float(c.n/a.n),day90_return_to_anchor=float(c.count_by_day90/a.count_by_day90),
 anchor_reviews_before_return=int(v[v.appid.eq(anchor)].created.lt(cutoff).sum()),
 quiet_reviews_before_return=int(v[v.appid.eq(quiet)].created.lt(cutoff).sum())))
pd.DataFrame(sequences).to_csv(R/'quiet_release_sequences.csv',index=False)
print('OWN WINDOWS\n',pd.DataFrame(windows).to_string(index=False))
print('SEQUENCES\n',pd.DataFrame(sequences).round(3).to_string(index=False))
print('BAND CHANGES',len(changes),'of',len(p))
