#!/usr/bin/env python3
from __future__ import annotations
import argparse, os, sqlite3, sys, time
from urllib.parse import quote_plus, urljoin, urlparse
import requests
from bs4 import BeautifulSoup
from extractors.generic import extract
from matching import confidence, norm, tokens, validate_product_match

DB=os.environ.get('FIYAT_RADAR_DB','/var/lib/fiyat-radar/fiyat-radar.sqlite')
UA=os.environ.get('FIYAT_RADAR_UA','Mozilla/5.0 (compatible; FiyatRadar/0.1.1; price-discovery)')
TIMEOUT=int(os.environ.get('FIYAT_RADAR_TIMEOUT','20'))


def connect():
    c=sqlite3.connect(DB,timeout=10); c.row_factory=sqlite3.Row
    c.execute('PRAGMA foreign_keys=ON'); c.execute('PRAGMA journal_mode=WAL'); c.execute('PRAGMA busy_timeout=5000')
    return c


def same_domain(url, domain):
    host=(urlparse(url).hostname or '').lower(); domain=(domain or '').lower()
    return bool(host and domain and (host==domain or host.endswith('.'+domain)))


def make_search_url(row):
    query=' '.join(x for x in [row['brand'],row['model'],row['variant']] if x).strip() or row['product_name']
    tpl=row['search_url_template'] or ''
    return tpl.replace('{query}',quote_plus(query)).replace('{ean}',quote_plus(row['ean'] or query))


def candidate_links(html, base_url, domain, product):
    soup=BeautifulSoup(html,'lxml')
    wanted=tokens(' '.join([product['brand'] or '',product['model'] or '',product['variant'] or '']))
    scored=[]; seen=set()
    for a in soup.find_all('a',href=True):
        u=urljoin(base_url,a['href']).split('#',1)[0]
        if u in seen or not same_domain(u,domain): continue
        seen.add(u)
        text=norm(a.get_text(' ',strip=True)+' '+u)
        score=sum(1 for t in wanted if t in text)
        if score<=0: continue
        scored.append((score,u))
    scored.sort(key=lambda x:x[0],reverse=True)
    return [u for _,u in scored[:12]]


def run(limit=20):
    c=connect()
    rows=c.execute('''SELECT p.id product_id,p.name product_name,p.brand,p.model,p.variant,p.ean,
        m.id merchant_id,m.name merchant,m.domain,m.trust_score,m.search_url_template
        FROM products p CROSS JOIN merchants m
        WHERE p.active=1 AND m.active=1 AND m.discovery_active=1 AND m.search_url_template IS NOT NULL
        AND NOT EXISTS (SELECT 1 FROM product_sources s WHERE s.product_id=p.id AND s.merchant_id=m.id AND s.active=1)
        ORDER BY p.updated_at DESC,p.id,m.id LIMIT ?''',(limit,)).fetchall()
    session=requests.Session(); session.headers.update({'User-Agent':UA,'Accept-Language':'tr-TR,tr;q=0.9,en;q=0.7'})
    auto=queued=failed=0
    for row in rows:
        try:
            search_url=make_search_url(row)
            if not same_domain(search_url,row['domain']): raise ValueError('Arama URL şablonu mağaza domainiyle eşleşmiyor')
            r=session.get(search_url,timeout=TIMEOUT); r.raise_for_status()
            product={k:row[k] for k in ('brand','model','variant','ean')}
            best=None
            for u in candidate_links(r.text,search_url,row['domain'],product)[:8]:
                try:
                    rr=session.get(u,timeout=TIMEOUT); rr.raise_for_status()
                    offer=extract(rr.text,u)
                    validate_product_match(product,offer.title)
                    conf=confidence(product,offer.title,rr.text)
                    if best is None or conf>best[0]: best=(conf,u,offer)
                    if conf>=0.85: break
                    time.sleep(.2)
                except Exception:
                    continue
            if not best: raise ValueError('Uygun ürün sayfası bulunamadı')
            conf,u,offer=best; effective=conf*(float(row['trust_score'])/100.0)
            if effective>=0.62 and conf>=0.65:
                c.execute("INSERT OR IGNORE INTO product_sources(product_id,merchant_id,url,extractor,active,check_interval_minutes,next_check_at) VALUES(?,?,?,'generic',1,360,datetime('now'))",(row['product_id'],row['merchant_id'],u))
                c.execute("INSERT OR IGNORE INTO discovery_candidates(product_id,merchant_id,url,title,price,currency,confidence,status,reviewed_at) VALUES(?,?,?,?,?,?,?,'auto_approved',CURRENT_TIMESTAMP)",(row['product_id'],row['merchant_id'],u,offer.title,offer.price,offer.currency,conf))
                auto+=1; print(f"AUTO {row['merchant']} | {row['product_name']} | {u} | güven={conf:.2f}")
            elif conf>=0.45:
                c.execute("INSERT OR IGNORE INTO discovery_candidates(product_id,merchant_id,url,title,price,currency,confidence,status) VALUES(?,?,?,?,?,?,?,'pending')",(row['product_id'],row['merchant_id'],u,offer.title,offer.price,offer.currency,conf))
                queued+=1; print(f"ADAY {row['merchant']} | {row['product_name']} | güven={conf:.2f}")
            else:
                failed+=1
            c.commit()
        except Exception as e:
            failed+=1; print(f"ERR {row['merchant']} | {row['product_name']} | {e}",file=sys.stderr)
        time.sleep(.5)
    c.close(); print(f'Keşif tamamlandı: {len(rows)} çift, {auto} otomatik, {queued} aday, {failed} başarısız')

if __name__=='__main__':
    ap=argparse.ArgumentParser(); ap.add_argument('--limit',type=int,default=20); a=ap.parse_args(); run(a.limit)
