from browser import document as d, html as h, window as w, timer
import json, time

DATABASE=json.loads(d['recipe-data'].text)
RECIPES=DATABASE['recipes']
state={'recipe':0,'recipe_id':'chili','servings':6,'variant':'vegan','step':0,'view':'home','checked':[],'timer_end':0,'timer_left':0,'timer_step':-1}
try:
 saved=json.loads(w.localStorage.getItem('fozzunk-session') or '{}')
 idx=next((i for i,r in enumerate(RECIPES) if r['id']==saved.get('recipe_id')),saved.get('recipe',0))
 if isinstance(idx,int) and 0<=idx<len(RECIPES) and saved.get('view') in ('home','detail','cook','done'):
  state.update(saved);state['recipe']=idx;state['recipe_id']=RECIPES[idx]['id']
  state['servings']=max(1,min(24,int(state['servings'])))
  state['step']=max(0,min(int(state['step']),len(RECIPES[idx]['steps'])))
  if state['view']=='cook' and (RECIPES[idx]['status']!='complete' or state['step']>=len(RECIPES[idx]['steps'])):state['view']='detail'
except Exception:pass
wake=None
wake_text='Ébrentartás kikapcsolva'
query=''
filter_status='all'
filter_category='all'
filter_vegan=False
STATUS={'complete':'Főzhető recept','partial':'Pontosítandó','idea':'Receptötlet'}
DIET={'vegan':'🌱 Vegán','vegetarian':'Vegetáriánus','meat':'Húsos','mixed':'Vegán + húsos','unknown':'Összetétel pontosítandó'}

def persist():
 state['recipe_id']=recipe()['id']
 try:w.localStorage.setItem('fozzunk-session',json.dumps(state))
 except Exception:pass

def button(text,fn,cls=''):
 b=h.BUTTON(text,Class=cls,type='button');b.bind('click',fn);return b

def recipe():return RECIPES[state['recipe']]
def number(n):return str(int(n)) if n==int(n) else str(round(n,2)).replace('.',',')
def amount(q):
 if q is None:return ''
 factor=state['servings']/recipe()['base']
 if isinstance(q,list):return '–'.join(number(n*factor) for n in q)
 return number(q*factor)
def portion_label(r=None,quantity=None):
 r=r or recipe();quantity=state['servings'] if quantity is None else quantity
 if r['unit'].startswith('×'):return number(quantity)+'× eredeti mennyiség'
 return number(quantity)+' '+r['unit']
def variant_label():
 if recipe().get('variants'):return 'vegán' if state['variant']=='vegan' else 'eredeti'
 return DIET[recipe()['diet']]
def ingredients():
 items=list(recipe()['ingredients'])
 if recipe().get('variants') and state['variant']=='original':
  if recipe()['id']=='chili':items[3]=[500,'g','darált pulykacomb']
  elif recipe()['id']=='pasta':
   items[2]=[100,'g','crème fraîche'];items[-1]=[None,'','Friss bazsalikom és reszelt parmezán a tálaláshoz']
 return items

def step_data(index=None):
 title,text,mins=recipe()['steps'][state['step'] if index is None else index]
 if recipe().get('variants') and state['variant']=='original':
  text=text.replace('növényi darált húst','darált pulykacombot').replace('Pirítsd meg a csomagoláson megadott útmutató szerint.','Pirítsd és süsd teljesen át.').replace('növényi krémet','crème fraîche-t').replace('vegán parmezán','reszelt parmezán')
 return title,text,mins

def wake_status(text):
 global wake_text
 wake_text=text
 if 'wake' in d:d['wake'].text=text

def release_wake():
 global wake
 lock=wake;wake=None
 wake_status('Ébrentartás kikapcsolva')
 if lock:
  try:lock.release()
  except Exception:pass

def wake_ok(lock):
 global wake
 if state['view']!='cook':lock.release();return
 wake=lock;wake_status('● A képernyő ébren marad')
 def released(e):
  global wake
  if wake==lock:wake=None;wake_status('○ Ébrentartás szünetel')
 lock.addEventListener('release',released)
def wake_fail(error=None):wake_status('○ Nem elérhető — állítsd hosszabbra a képernyőzárat')
def request_wake(e=None):
 if state['view']!='cook' or wake:return
 try:w.navigator.wakeLock.request('screen').then(wake_ok,wake_fail)
 except Exception:wake_fail()
def visibility(e):
 if d.visibilityState=='visible' and state['view']=='cook':request_wake()
d.bind('visibilitychange',visibility)

def go(view):
 if view=='cook' and recipe()['status']!='complete':view='detail'
 state['view']=view;persist()
 if view!='cook':release_wake()
 render();w.scrollTo(0,0)
 if view=='cook':request_wake()
def select(index):
 if state['recipe']!=index or state['view'] in ('home','done'):
  state.update(recipe=index,servings=RECIPES[index]['base'],step=0,checked=[],timer_end=0,timer_left=0,timer_step=-1,variant='vegan')
 go('detail')
def servings(delta):
 state['servings']=max(1,min(24,state['servings']+delta));state['checked']=[];persist();render()
def variant(e):state['variant']=e.target.value;state['checked']=[];persist();render()
def check(e,index):
 if e.target.checked:
  if index not in state['checked']:state['checked'].append(index)
 elif index in state['checked']:state['checked'].remove(index)
 persist()
def ingredient_list():
 wrap=h.DIV()
 for i,(q,unit,name) in enumerate(ingredients()):
  label=h.LABEL(Class='ingredient');box=h.INPUT(type='checkbox');box.checked=i in state['checked'];box.bind('change',lambda e,i=i:check(e,i));label<=box;label<=h.SPAN(f'{amount(q)} {unit} {name}'.strip());wrap<=label
 if not ingredients():wrap<=h.P('Hozzávalólista nem került elő.',Class='muted')
 return wrap

# Self-contained normalization: Brython's unicodedata.combining requires an
# additional unicode.txt download that is not part of this static application.
SEARCH_ACCENTS=str.maketrans('áàâäãåéèêëíìîïóòôöőõúùûüűçñýÿ','aaaaaaeeeeiiiioooooouuuuucnyy')
def normalized(text):
 text=text.lower().translate(SEARCH_ACCENTS)
 text=''.join(c for c in text if not 0x0300<=ord(c)<=0x036f)
 text=' '.join(''.join(c if c.isalnum() or c=='-' else ' ' for c in text).split())
 return text.replace('miso','miszo')
SEARCH_INDEX={r['id']:normalized(' '.join([r['name'],r['category'],r['source']]+[item[2] for item in r['ingredients']])) for r in RECIPES}
def matching(r):
 terms=normalized(query).split()
 return all(term in SEARCH_INDEX[r['id']] for term in terms) and (filter_status=='all' or r['status']==filter_status) and (filter_category=='all' or r['category']==filter_category) and (not filter_vegan or r['diet']=='vegan')
def search_changed(e):
 global query
 query=e.target.value;cards()
def set_status(value):
 global filter_status
 filter_status=value;render()
def set_category(e):
 global filter_category
 filter_category=e.target.value;cards()
def toggle_vegan(e):
 global filter_vegan
 filter_vegan=not filter_vegan;render()
def reset_filters(e=None):
 global query,filter_category,filter_status,filter_vegan
 query='';filter_category='all';filter_status='all';filter_vegan=False;render()

def home():
 root=d['app'];hero=h.DIV(Class='hero');copy=h.DIV();copy<=h.DIV('A TE KONYHÁD, A TE TEMPÓDBAN',Class='eyebrow');copy<=h.H1('Ma mit főzzünk?');copy<=h.P('A közös beszélgetéseink receptjei, egy helyen. Válassz kedvencet, állítsd be a mennyiséget, és főzzünk együtt.',Class='muted');hero<=copy;hero<=h.DIV('🌿',Class='hero-mark');root<=hero
 if 0<state['step']<len(recipe()['steps']) and recipe()['status']=='complete':root<=button('↗ Folytatom: '+recipe()['name'],lambda e:go('cook'),'resume')
 tabs=h.DIV(Class='tabs')
 for key,label in [('all','Mind'),('complete','Főzhető'),('partial','Pontosítandó'),('idea','Ötletek')]:
  count=sum(key=='all' or r['status']==key for r in RECIPES)
  tabs<=button(f'{label} · {count}',lambda e,key=key:set_status(key),'active' if filter_status==key else '')
 root<=tabs
 toolbar=h.DIV(Class='toolbar');search=h.INPUT(type='search',placeholder='Recept, hozzávaló vagy dátum…',value=query);search.attrs['aria-label']='Recept keresése';search.bind('input',search_changed);toolbar<=search
 category=h.SELECT();category.attrs['aria-label']='Receptkategória'
 for value,label in [('all','Minden kategória')]+[(x,x.capitalize()) for x in sorted(set(r['category'] for r in RECIPES))]:
  option=h.OPTION(label,value=value);option.selected=value==filter_category;category<=option
 category.bind('change',set_category);toolbar<=category;toolbar<=button('🌱 Vegán',toggle_vegan,'active' if filter_vegan else '');root<=toolbar
 root<=h.P('',Id='results',Class='muted small');root<=h.DIV(Id='cards',Class='cards');cards()
 details=h.DETAILS(Class='note');details<=h.SUMMARY('A receptgyűjteményről és az importról');details<=h.P(DATABASE['metadata']['scope']);details<=h.P('A hiányos leírások és ötletek külön jelölést kaptak. A forrás dátuma minden receptnél látható. A mennyiségek közötti tartományokat megőriztük; az időzítő a megadott időtartomány elején jelez, ekkor ellenőrizd az ételt.');details<=h.A('Receptadatbázis letöltése (JSON)',href='recipes.json',download='bazsci-receptek.json');root<=details

def cards():
 found=[(i,r) for i,r in enumerate(RECIPES) if matching(r)];target=d['cards'];target.clear()
 d['results'].text=f'{len(found)} találat · {len(RECIPES)} bejegyzés a gyűjteményben'
 for i,r in found:
  card=h.ARTICLE(Class='card');food=h.DIV(Class='food'+(' tomato' if i%2 else ''));food<=h.SPAN(STATUS[r['status']],Class='badge '+r['status']);food<=h.DIV(r['emoji'],Class='plate');card<=food
  body=h.DIV(Class='card-body');body<=h.DIV(r['category'],Class='eyebrow');body<=h.H3(r['name']);body<=h.P(DIET[r['diet']]+(' · eredeti változattal' if r.get('variants') else ''),Class='muted small')
  meta=r.get('yield_text') or portion_label(r,r['base'])
  if r.get('minutes'):meta='◷ '+r['minutes']+' perc · '+meta
  body<=h.DIV(meta,Class='meta');bottom=h.DIV(Class='card-bottom');bottom<=h.SPAN(r['source'],Class='meta');bottom<=button('Megfőzöm ↗' if r['status']=='complete' else 'Megnézem ↗',lambda e,i=i:select(i));body<=bottom;card<=body;target<=card
 if not found:target<=h.P('Nincs ilyen recept a kiválasztott szűrőkkel.',Class='muted');target<=button('Szűrők törlése',reset_filters)

def detail():
 root=d['app'];r=recipe();root<=button('← Receptjeim',lambda e:go('home'),'back');head=h.DIV(Class='detail-head');head<=h.DIV(r['category'],Class='eyebrow');head<=h.H1(r['name']);head<=h.P(STATUS[r['status']]+' · '+DIET[r['diet']]+' · Forrás: '+r['source'],Class='muted')
 if r.get('yield_text'):head<=h.P('Eredeti mennyiség: '+r['yield_text'],Class='meta')
 if r.get('minutes'):head<=h.P('◷ '+r['minutes']+' perc',Class='meta')
 root<=head
 if r['missing']:
  note=h.DIV(Class='note warning');note<=h.STRONG('Még pontosítandó');listing=h.UL()
  for text in r['missing']:listing<=h.LI(text)
  note<=listing;root<=note
 layout=h.DIV(Class='layout');panel=h.ASIDE(Class='panel');panel<=h.H3('Hozzávalók')
 if r['ingredients']:
  row=h.DIV(Class='servings');minus=button('−',lambda e:servings(-1));minus.disabled=state['servings']<=1;plus=button('+',lambda e:servings(1));plus.disabled=state['servings']>=24;row<=minus;row<=h.STRONG(portion_label());row<=plus;panel<=row
 if r.get('variants'):
  sel=h.SELECT(Class='switch');sel.attrs['aria-label']='Recept változata'
  for val,title in [('vegan','🌱 Vegán változat'),('original','Eredeti változat')]:
   opt=h.OPTION(title,value=val);opt.selected=state['variant']==val;sel<=opt
  sel.bind('change',variant);panel<=sel
 panel<=ingredient_list();panel<=h.P('Csak a számszerű mennyiségek változnak. A főzési időt, edényméretet és fűszerezést igazítsd a mennyiséghez.',Class='muted small');layout<=panel
 section=h.SECTION();section<=h.H2('Így készül' if r['status']=='complete' else 'Ami a leírásból megvan')
 if r['steps']:
  listing=h.OL(Class='step-list')
  for index in range(len(r['steps'])):
   title,text,mins=step_data(index);listing<=h.LI(h.STRONG(title)+h.DIV(text))
  section<=listing
 else:section<=h.P('Elkészítési útmutató nem került elő. A bejegyzést megőriztük, de még nem indítható főzési módban.',Class='muted')
 for text in r['notes']:section<=h.P(text,Class='note')
 if r.get('source_url'):section<=h.A(r.get('source_link_label','Az eredeti receptalkalmazás megnyitása ↗'),href=r['source_url'],target='_blank',rel='noopener noreferrer')
 if r['status']=='complete':section<=button('Főzés indítása →',lambda e:start_cooking(),'primary wide');section<=h.DIV('☀ Főzés közben megpróbáljuk ébren tartani a képernyőt. Maradj ezen az oldalon.',Class='note')
 else:section<=h.P('A vezetett főzés a hiányzó adatok kiegészítése után lesz elérhető.',Class='small muted')
 layout<=section;root<=layout

def start_cooking():
 if recipe()['status']!='complete':return
 state.update(step=0,timer_end=0,timer_left=0,timer_step=-1);go('cook')
def next_step(delta):
 state['step']=max(0,state['step']+delta)
 if state['step']>=len(recipe()['steps']):state.update(timer_end=0,timer_left=0);go('done')
 else:persist();render();w.scrollTo(0,0)
def timer_start(minutes):
 seconds=state['timer_left'] if state['timer_left']>0 else minutes*60
 state.update(timer_end=time.time()+seconds,timer_left=0,timer_step=state['step']);persist();render()
def timer_pause(e=None):state['timer_left']=max(0,int(state['timer_end']-time.time()+0.99));state['timer_end']=0;persist();render()
def timer_reset(e=None):state.update(timer_end=0,timer_left=0,timer_step=-1);persist();render()
def cook():
 root=d['app'];wrap=h.SECTION(Class='cook');r=recipe();top=h.DIV(Class='cook-top');top<=button('← Recepthez',lambda e:go('detail'),'back');badge=button(wake_text,request_wake,'wake');badge.id='wake';top<=badge;wrap<=top
 wrap<=h.DIV(r['name']+' · '+portion_label()+' · '+variant_label(),Class='meta');progress=h.DIV(Class='progress');progress<=h.DIV(style={'width':str(100*(state['step']+1)/len(r['steps']))+'%'});wrap<=progress
 title,text,mins=step_data();wrap<=h.DIV(f"{state['step']+1:02d} / {len(r['steps']):02d} LÉPÉS",Class='eyebrow');wrap<=h.H2(title,Class='step-title');wrap<=h.P(text,Class='instruction')
 if mins or state['timer_end'] or state['timer_left']:
  box=h.DIV(Class='timer',Id='timerbox');box<=h.DIV('FŐZÉSI IDŐZÍTŐ',Class='eyebrow');row=h.DIV(Class='timer-row');row<=h.STRONG('',Id='clock',Class='clock')
  if state['timer_end']:row<=button('Szünet',timer_pause)
  else:row<=button('Folytatás' if state['timer_left'] else 'Indítás',lambda e:timer_start(mins or 1))
  row<=button('Nullázás',timer_reset);box<=row;box<=h.DIV('Időtartománynál az első időpontban jelzünk: ellenőrizd az ételt.',Id='timerhint',Class='small')
  if (state['timer_end'] or state['timer_left']) and state['timer_step']!=state['step']:box<=h.P('Az időzítő egy korábbi lépéshez tartozik.',Class='small')
  wrap<=box
 details=h.DETAILS();details<=h.SUMMARY('Hozzávalók · '+portion_label());details<=ingredient_list();wrap<=details
 nav=h.DIV(Class='cook-nav');prev=button('← Előző',lambda e:next_step(-1));prev.disabled=state['step']==0;nav<=prev;nav<=button('Elkészült! ✓' if state['step']==len(r['steps'])-1 else 'Következő →',lambda e:next_step(1),'primary');wrap<=nav;root<=wrap;tick()
def done():
 box=h.DIV(Class='done');box<=h.DIV('🍽️',Class='done-icon');box<=h.DIV('KÉSZEN VAGY!',Class='eyebrow');box<=h.H1('Jó étvágyat!');box<=h.P(recipe()['name']+' · '+portion_label(),Class='muted');box<=button('Vissza a receptjeimhez',lambda e:finish(),'primary');d['app']<=box
def finish():state.update(step=0,checked=[]);go('home')
def tick():
 if 'clock' not in d:return
 mins=recipe()['steps'][min(state['step'],len(recipe()['steps'])-1)][2]
 left=max(0,int(state['timer_end']-time.time()+0.99)) if state['timer_end'] else (state['timer_left'] or mins*60)
 d['clock'].text=f'{int(left)//60:02d}:{int(left)%60:02d}'
 if state['timer_end'] and left<=0:d['timerbox'].class_name='timer timer-done';d['timerhint'].text='✓ Lejárt az idő! Ellenőrizd az ételt, majd folytasd.';d['clock'].text='Kész!'
def render():
 d['app'].clear();{'home':home,'detail':detail,'cook':cook,'done':done}[state['view']]()
d['home'].bind('click',lambda e:go('home'))
render()
if state['view']=='cook':request_wake()
timer.set_interval(tick,500)
