from curl_cffi import requests
from concurrent.futures import ThreadPoolExecutor, as_completed
from queue import Queue
from pathlib import Path
from urllib.parse import quote
import json,re,time,random,string
BASE='https://mein-portal.baloise.de/auth'
HEAD={'X-Bug-Bounty':'dwbzn','X-Same-Domain':'1','Accept':'application/json, text/plain, */*'}
terms='''
ao mo si zeus bvs mmag mmakler makler info infoportal mein meinportal portal vertriebsservice vertrieb vertriebs partner partnerportal maklerportal kunden kundenportal broker brokerportal sales salesportal service services antrag antragsfreigabe vertrag vertraege schaden versicherung baloise basler helvetia easylogin easy-login easy_login el account accountlinking linking bipro tgic crm bsi bsi_crm venta sas va ocs gw gwde guidewire pc bc cc cm policycenter billingcenter claimcenter contactmanager insurehub admin adminapp administrator management config configuration api rest web mobile login sso saml saml2 oidc oauth oauth2 registration registrierung selfservice password reset change migration default public protected internal external ext extlogin external-login demo debug test testing dev prod production application app target targetapp portalredirect redirect richfaces liferay legacy new old frontend backend servicecontainer service-container apipolicy api-policy transaction approval taa iam airlock
'''.split()
base=set(terms)
# Product/location-derived compounds.
base |= set('''portal_ao portal_mo portal_si portal_zeus portal_bvs portal_mmag portal_mmakler portal_info portal_mein portal_makler portal_vertrieb portal_kunden portal_broker portal_partner portal_bipro portal_ocs portal_tgic portal_crm portal_bsi guidewire_pc guidewire_bc guidewire_cc guidewire_cm gw_pc gw_bc gw_cc gw_cm tgtapp-gw-pc tgtapp-gw-bc tgtapp-gw-cc tgtapp-gw-cm tgtapp-bsi-crm tgtapp-antragsfreigabe tgtapp-insurehub el_account_linking el_account_linking2 account_linking account-linking easylogin_baloise easylogin-baloise baloise_easylogin baloise-easylogin saml_easylogin saml-easylogin bipro_baloise bipro-baloise bvs_portal zeus_portal ao_portal mo_portal si_portal customer employee agent employeeportal agentportal partner-login broker-login makler-login internal-portal internal_portal admin-portal admin_portal test-portal test_portal demo-portal demo_portal debug-portal debug_portal'''.split())
# Systematic restrained combinations.
for t in list(terms):
    for pre in ('portal_','app_','tgtapp-','target-'):
        base.add(pre+t)
    for suf in ('_portal','-portal','_app','-app'):
        base.add(t+suf)
for a in ('guidewire','gw'):
    for b in ('pc','bc','cc','cm','policycenter','billingcenter','claimcenter','contactmanager'):
        for sep in ('_','-',''):
            base.add(a+sep+b); base.add('portal_'+a+sep+b); base.add('tgtapp-'+a+sep+b)
for a in ('portal','baloise','basler','easylogin','bipro','iam'):
    for b in ('ao','mo','si','zeus','bvs','mmag','venta','crm','sso','login','prod','internal'):
        for sep in ('_','-'):
            base.add(a+sep+b); base.add(b+sep+a)
# exact route names with case variants only where plausible.
for x in list(base):
    if len(x)<=30 and x.islower() and ('easy' in x or x in {'portal','admin','default','baloise','basler'}):
        base.add(x[:1].upper()+x[1:])
apps=sorted(x for x in base if 1<=len(x)<=30 and re.fullmatch(r'[A-Za-z0-9_-]+',x))
known=set('''2fa EasyLogin Easylogin Foobar Portal_ao admin adminapp airlock-console antragsfreigabe auth-admin bsi-crm bsi_crm change-email-insert change-password default easy-login easy_login easylogin el-account-linking el-account-linking2 el_account_linking el_account_linking2 ext ext-login foobar guidewire_bc guidewire_cc guidewire_cm guidewire_pc iam-admin insurehub invalid-flow ocs ocs_bvs one-shot oneshot portal-application-id portal_ao portal_bvs portal_ext portal_mo portal_ocs portal_si portal_zeus represented sas_va sso sso-login sso-ticket sso_ticket tgtapp-antragsfreigabe tgtapp-bsi-crm tgtapp-gw-bc tgtapp-gw-pc tgtapp-insurehub ticket venta zeus'''.split())
apps=[x for x in apps if x not in known]
Path('work/scratch/selector-candidates.txt').write_text('\n'.join(apps))
print('candidates',len(apps),flush=True)

def worker(chunk):
    s=requests.Session(impersonate='chrome')
    s.get(BASE+'/ui/app',headers={'X-Bug-Bounty':'dwbzn'},verify=False,timeout=20,toadex_capture=False)
    out=[]
    for app in chunk:
        csrf=s.cookies.get('CSRFT759-S') or ''
        h={**HEAD,'X-CSRFT759':csrf,'Content-Type':'application/json'}
        try:
            s.delete(BASE+'/rest/public/authentication/flow',headers=h,verify=False,timeout=15,toadex_capture=False)
            csrf=s.cookies.get('CSRFT759-S') or csrf; h['X-CSRFT759']=csrf
            r=s.post(BASE+'/rest/public/authentication/applications/'+quote(app,safe='')+'/access',headers=h,data=b'{}',verify=False,timeout=20,toadex_capture=False)
            if r.status_code!=404:
                code=''; nxt=''; step=''; path=''
                try:
                    j=r.json();
                    errs=j.get('errors') or []
                    if errs: code=errs[0].get('code','')
                    d=j.get('data') or {}; at=d.get('attributes') or {}
                    nxt=str(at.get('nextStep') or at.get('next-step') or at.get('status') or '')
                    step=str(at.get('stepId') or at.get('step-id') or '')
                    path=str(at.get('path') or '')
                except Exception: pass
                out.append((app,r.status_code,len(r.content),code,nxt,step,path,r.text[:300].replace('\n',' ')))
        except Exception as e:
            out.append((app,0,0,'ERR','','','',repr(e)))
    return out
nworkers=10
chunks=[apps[i::nworkers] for i in range(nworkers)]
hits=[]
with ThreadPoolExecutor(max_workers=nworkers) as ex:
    for f in as_completed([ex.submit(worker,c) for c in chunks]):
        rows=f.result(); hits.extend(rows)
        for row in rows: print('\t'.join(map(str,row)),flush=True)
Path('work/scratch/selector-hits.json').write_text(json.dumps(hits,indent=2))
print('done hits',len(hits),flush=True)
