import secrets, time, csv, io, hmac
from datetime import datetime, timezone
from functools import wraps
from flask import Flask, request, session, redirect, url_for, render_template, Response, abort
from core import *
c=config(); app=Flask(__name__); app.secret_key=c['secret_key']
app.config.update(SESSION_COOKIE_SECURE=True,SESSION_COOKIE_HTTPONLY=True,SESSION_COOKIE_SAMESITE='Lax',MAX_CONTENT_LENGTH=16384)

def auth(f):
    @wraps(f)
    def wrapped(*a,**kw):
        if not session.get('authenticated'): return redirect(url_for('login'))
        return f(*a,**kw)
    return wrapped

@app.after_request
def headers(r):
    r.headers['Cache-Control']='no-store'; r.headers['X-Content-Type-Options']='nosniff'
    r.headers['X-Frame-Options']='DENY'; r.headers['Referrer-Policy']='no-referrer'
    r.headers['Content-Security-Policy']="default-src 'self'; style-src 'self' 'unsafe-inline'; form-action 'self'; frame-ancestors 'none'"
    return r

@app.template_filter('date')
def date(t):
    return datetime.fromtimestamp(t,timezone.utc).strftime('%d %b %Y %H:%M UTC') if t else 'Not yet'

@app.route('/login',methods=['GET','POST'])
def login():
    session.setdefault('csrf',secrets.token_hex(32)); error=None
    if request.method=='POST':
        if not hmac.compare_digest(request.form.get('csrf',''),session['csrf']): abort(400)
        db=connect(c); init(db)
        ip=hashlib.sha256((request.remote_addr or 'unknown').encode()).hexdigest()
        try:
            with db.cursor() as q:
                q.execute('SELECT * FROM cr_login WHERE ip=%s',(ip,)); row=q.fetchone()
                blocked=row and row['until_ts']>time.time()
                if not blocked and hmac.compare_digest(request.form.get('username',''),c['dashboard_user']) and hmac.compare_digest(request.form.get('password',''),c['dashboard_password']):
                    q.execute('DELETE FROM cr_login WHERE ip=%s',(ip,)); session.clear(); session['authenticated']=True; session['csrf']=secrets.token_hex(32)
                    return redirect(url_for('dashboard'))
                if not blocked:
                    failures=(row['failures'] if row else 0)+1
                    q.execute('REPLACE INTO cr_login VALUES(%s,%s,%s)',(ip,failures, time.time()+900 if failures>=5 else 0))
                error='Login failed. After repeated attempts, wait 15 minutes.'
        finally: db.close()
    return render_template('login.html',error=error)

@app.post('/logout')
@auth
def logout():
    if not hmac.compare_digest(request.form.get('csrf',''),session.get('csrf','')): abort(400)
    session.clear(); return redirect(url_for('login'))

@app.get('/')
@auth
def dashboard():
    db=connect(c)
    try:
        init(db); groups,rows=report(db,c)
        info={k:state(db,k) for k in ('last_run','scan_completed','error','analysis_error','analysis_mode')}
        with db.cursor() as q:
            q.execute('SELECT COUNT(*) AS n FROM cr_chats WHERE dirty=1 AND activity>=%s',(time.time()-86400,)); info['pending']=q.fetchone()['n']
        info['stale']=not info['last_run'] or time.time()-info['last_run']>900
        return render_template('dashboard.html',groups=groups,rows=rows,info=info,c=c,now=time.time(),refresh=max(30,int(c.get('refresh_seconds',60))))
    finally: db.close()

@app.get('/export.csv')
@auth
def export():
    db=connect(c)
    try: groups,rows=report(db,c)
    finally: db.close()
    buf=io.StringIO(); w=csv.writer(buf); w.writerow(['Category','Chats','Resolved','Pending','Examples','Resolution evidence'])
    def safe(s):
        s=str(s); return "'"+s if s.lstrip().startswith(('=','+','-','@')) else s
    for g in groups: w.writerow([safe(g['name']),g['count'],g['resolved'],g['pending'],safe(' | '.join(g['examples'])),safe(' | '.join(g['resolutions']))])
    return Response(buf.getvalue(),mimetype='text/csv',headers={'Content-Disposition':'attachment; filename=chatwoot-24h.csv'})
