"""
AutoLyrics OneClick - Web Server & Payment Orchestration Framework
Supports VNPAY, VISA/Mastercard (Stripe), and Direct Downloads for AutoLyricsSetup.exe
"""

import os
import uuid
import random
import string
import time
from datetime import datetime, timedelta
from flask import (
    Flask, render_template, request, jsonify, 
    send_file, send_from_directory, redirect, url_for, session
)

import config
import payment_config
import database
import make_key
from vnpay import VNPay

# Initialize Flask app
app = Flask(
    __name__, 
    template_folder='templates',
    static_folder='static',
    static_url_path='/static'
)
app.secret_key = config.SECRET_KEY
app.config['SESSION_COOKIE_HTTPONLY'] = True
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(hours=4)

# In-memory brute force protection for admin login: {ip: {'count': int, 'locked_until': float}}
admin_failed_attempts = {}

def get_real_client_ip():
    """Extract real client IP address behind proxies like Cloudflare or LiteSpeed."""
    raw_ip = request.headers.get('CF-Connecting-IP') or request.headers.get('X-Forwarded-For') or request.remote_addr or '127.0.0.1'
    return raw_ip.split(',')[0].strip() if ',' in raw_ip else raw_ip.strip()

# Initialize database and CSV header on startup
database.init_db()

# Initialize VNPAY Client
vnpay_client = VNPay(
    tmn_code=payment_config.VNPAY_TMN_CODE,
    hash_secret=payment_config.VNPAY_HASH_SECRET,
    payment_url=payment_config.VNPAY_PAYMENT_URL,
    return_url=payment_config.VNPAY_RETURN_URL
)

def generate_license_key(user_identifier: str = None):
    """Generate cryptographic HMAC-SHA256 license key matching the app algorithm in make_key.py."""
    if user_identifier and user_identifier.strip():
        return make_key.generate_license(user_identifier.strip())
    return make_key.generate_license("customer@autolyrics.app")

def get_distributable_file():
    """Find the best available installer or package to distribute."""
    # 1. Primary: AutoLyricsSetup.exe in downloads/
    exe_path = os.path.join(config.DOWNLOADS_DIR, "AutoLyricsSetup.exe")
    if os.path.exists(exe_path):
        return exe_path, "AutoLyricsSetup.exe"

    # 2. Check source/NEW UPDATE/ folder
    new_update_exe = os.path.join(config.SOURCE_DIR, "NEW UPDATE", "AutoLyricsSetup.exe")
    if os.path.exists(new_update_exe):
        return new_update_exe, "AutoLyricsSetup.exe"

    # 3. Check source/ folder directly for AutoLyricsSetup.exe
    source_exe = os.path.join(config.SOURCE_DIR, "AutoLyricsSetup.exe")
    if os.path.exists(source_exe):
        return source_exe, "AutoLyricsSetup.exe"

    # 4. Any other .exe in downloads folder
    if os.path.exists(config.DOWNLOADS_DIR):
        for f in os.listdir(config.DOWNLOADS_DIR):
            if f.lower().endswith('.exe'):
                return os.path.join(config.DOWNLOADS_DIR, f), f

    # 5. Fallback to zip package
    fallback = os.path.join(config.DOWNLOADS_DIR, config.DEFAULT_ZIP_NAME)
    return fallback, os.path.basename(fallback)

# ============================================================================
# Core Storefront & Static Routes
# ============================================================================

@app.route('/')
def home():
    """Serve the sales landing page."""
    file_path, file_name = get_distributable_file()
    return render_template(
        'index.html',
        app_name=config.APP_NAME,
        app_version=config.APP_VERSION,
        base_price=f"{config.APP_PRICE:.2f}",
        currency=config.CURRENCY,
        dist_file=file_name,
        vnd_rate=payment_config.USD_TO_VND_RATE,
        vietqr_bank=payment_config.VIETQR_BANK_ID,
        vietqr_bank_name=payment_config.VIETQR_BANK_NAME,
        vietqr_account=payment_config.VIETQR_ACCOUNT_NO,
        vietqr_name=payment_config.VIETQR_ACCOUNT_NAME
    )

@app.route('/my-license', methods=['GET', 'POST'])
def my_license():
    """Customer portal to view bought product, license key, and download app."""
    email_query = request.args.get('email', '').strip()
    order_id_query = request.args.get('order_id', '').strip()
    
    searched_email = None
    orders = []
    error = None

    if request.method == 'POST':
        identifier = request.form.get('identifier', '').strip()
    else:
        identifier = email_query or order_id_query

    if identifier:
        searched_email = identifier
        # 1. Search by email
        orders = database.get_orders_by_email(identifier)

        # 2. Search by order ID or license key
        if not orders:
            single = database.get_order_by_id(identifier) or database.get_order_by_key(identifier)
            if single:
                orders = [single]
        
        if not orders:
            error = f"Không tìm thấy đơn hàng nào liên kết với '{identifier}'. Vui lòng kiểm tra lại email bạn đã dùng khi thanh toán."

    file_path, file_name = get_distributable_file()
    
    return render_template(
        'my_license.html',
        app_name=config.APP_NAME,
        app_version=config.APP_VERSION,
        dist_file=file_name,
        orders=orders,
        searched_email=searched_email,
        error=error
    )

@app.route('/api/lookup-license', methods=['POST'])
def api_lookup_license():
    """API for dynamic lookup of customer purchases and keys."""
    data = request.get_json() or request.form
    identifier = data.get('identifier', '').strip()
    if not identifier:
        return jsonify({'success': False, 'error': 'Vui lòng nhập email hoặc mã đơn hàng.'}), 400

    orders = database.get_orders_by_email(identifier)
    if not orders:
        single = database.get_order_by_id(identifier) or database.get_order_by_key(identifier)
        if single:
            orders = [single]

    if not orders:
        return jsonify({'success': False, 'error': 'Không tìm thấy đơn hàng nào với thông tin này.'}), 404

    file_path, file_name = get_distributable_file()
    return jsonify({
        'success': True,
        'orders': orders,
        'file_name': file_name
    })


@app.route('/api/payment-config')
def get_payment_config():
    """Return public payment gateway settings for VietQR and currency rates."""
    return jsonify({
        'vietqr_bank': payment_config.VIETQR_BANK_ID,
        'vietqr_bank_name': payment_config.VIETQR_BANK_NAME,
        'vietqr_account': payment_config.VIETQR_ACCOUNT_NO,
        'vietqr_name': payment_config.VIETQR_ACCOUNT_NAME,
        'usd_to_vnd_rate': payment_config.USD_TO_VND_RATE,
        'base_price_usd': config.APP_PRICE
    })

@app.route('/styles.css')
def serve_css():
    return send_from_directory(os.path.dirname(__file__), 'styles.css')

@app.route('/app.js')
def serve_js():
    return send_from_directory(os.path.dirname(__file__), 'app.js')

@app.route('/assets/<path:filename>')
def serve_assets(filename):
    assets_dir = os.path.join(os.path.dirname(__file__), 'assets')
    return send_from_directory(assets_dir, filename)


# ============================================================================
# Order & Payment APIs (VISA / Card / Instant)
# ============================================================================

@app.route('/api/create-order', methods=['POST'])
def create_order():
    """
    Process VISA/Card payment, store customer in SQLite, append to CSV, 
    and return unique license key with download link for AutoLyricsSetup.exe.
    """
    try:
        data = request.get_json() or request.form
        
        email = data.get('customer_email', '').strip()
        if not email or '@' not in email:
            return jsonify({'success': False, 'error': 'A valid customer email is required.'}), 400

        name = data.get('customer_name', '').strip() or 'Creator'
        base_price = float(data.get('base_price', config.APP_PRICE))
        
        try:
            tip_amount = max(0.0, float(data.get('tip_amount', 0.0)))
        except (ValueError, TypeError):
            tip_amount = 0.0
            
        total_amount = round(base_price + tip_amount, 2)
        payment_method = data.get('payment_method', 'Visa / Card')
        
        # Order metadata
        order_id = f"ORD-{datetime.now().strftime('%Y%m%d')}-{random.randint(10000, 99999)}"
        license_key = generate_license_key(email)
        download_token = str(uuid.uuid4())
        client_ip = request.remote_addr or '127.0.0.1'

        order_data = {
            'order_id': order_id,
            'customer_name': name,
            'customer_email': email,
            'base_price': base_price,
            'tip_amount': tip_amount,
            'total_amount': total_amount,
            'payment_method': payment_method,
            'payment_status': 'COMPLETED',
            'license_key': license_key,
            'download_token': download_token,
            'ip_address': client_ip
        }

        # Save to SQLite and CSV simultaneously
        database.save_order(order_data)

        file_path, file_name = get_distributable_file()

        return jsonify({
            'success': True,
            'order_id': order_id,
            'customer_name': name,
            'customer_email': email,
            'base_price': base_price,
            'tip_amount': tip_amount,
            'total_amount': total_amount,
            'license_key': license_key,
            'download_url': f"/download/{download_token}",
            'file_name': file_name
        })

    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500

# ============================================================================
# VNPAY Gateway API & Return Callback
# ============================================================================

@app.route('/api/create-vnpay-payment', methods=['POST'])
def create_vnpay_payment():
    """Generate VNPAY checkout URL for QR Banking & ATM cards."""
    try:
        data = request.get_json() or request.form
        email = data.get('customer_email', '').strip()
        if not email or '@' not in email:
            return jsonify({'success': False, 'error': 'Vui lòng nhập email hợp lệ.'}), 400

        name = data.get('customer_name', '').strip() or 'Khách hàng'
        base_price = float(data.get('base_price', config.APP_PRICE))
        try:
            tip_amount = max(0.0, float(data.get('tip_amount', 0.0)))
        except (ValueError, TypeError):
            tip_amount = 0.0

        total_usd = round(base_price + tip_amount, 2)
        amount_vnd = int(total_usd * payment_config.USD_TO_VND_RATE)

        order_id = f"VNP{datetime.now().strftime('%Y%m%d%H%M%S')}{random.randint(10, 99)}"
        license_key = generate_license_key(email)
        # Extract real client public IP (supports Cloudflare, LiteSpeed, and reverse proxies)
        raw_ip = request.headers.get('CF-Connecting-IP') or request.headers.get('X-Forwarded-For') or request.remote_addr or '127.0.0.1'
        client_ip = raw_ip.split(',')[0].strip() if ',' in raw_ip else raw_ip.strip()

        order_data = {
            'order_id': order_id,
            'customer_name': name,
            'customer_email': email,
            'base_price': base_price,
            'tip_amount': tip_amount,
            'total_amount': total_usd,
            'payment_method': 'VNPAY (QR / ATM / Visa)',
            'payment_status': 'PENDING',
            'license_key': license_key,
            'download_token': download_token,
            'ip_address': client_ip
        }
        database.save_order(order_data)

        order_desc = f"AutoLyrics App - {order_id}"
        vnp_url = vnpay_client.build_payment_url(order_id, amount_vnd, order_desc, client_ip)

        return jsonify({
            'success': True,
            'payment_url': vnp_url,
            'order_id': order_id,
            'amount_vnd': amount_vnd
        })

    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500

@app.route('/vnpay_return')
def vnpay_return():
    """Handle customer redirect back from VNPAY after payment."""
    params = request.args.to_dict()
    result = vnpay_client.validate_response(params)
    order_id = result.get('order_id')
    is_success = result.get('is_success')

    conn = database.get_db_connection()
    cur = conn.cursor()
    cur.execute('SELECT * FROM orders WHERE order_id = ?', (order_id,))
    row = cur.fetchone()

    if row and is_success:
        cur.execute("UPDATE orders SET payment_status = 'COMPLETED' WHERE order_id = ?", (order_id,))
        conn.commit()
        order = dict(row)
        conn.close()

        file_path, file_name = get_distributable_file()
        return render_template(
            'payment_success.html',
            order=order,
            file_name=file_name,
            download_url=f"/download/{order['download_token']}"
        )
    else:
        conn.close()
        return render_template('payment_failed.html', error_code=result.get('response_code', '99'))

# ============================================================================
# Secure File Download Route (Delivers AutoLyricsSetup.exe)
# ============================================================================

@app.route('/download/<token>')
def download_app(token):
    """Serve AutoLyricsSetup.exe to verified customers."""
    order = database.get_order_by_token(token)
    if not order:
        return "Invalid or expired download token. Please contact support with your receipt.", 404

    file_path, file_name = get_distributable_file()
    
    if not os.path.exists(file_path):
        return f"File '{file_name}' not found on server.", 404

    return send_file(
        file_path,
        as_attachment=True,
        download_name=file_name
    )

# ============================================================================
# Admin Dashboard & CSV Export
# ============================================================================

@app.route('/admin', methods=['GET', 'POST'])
def admin_dashboard():
    """Admin dashboard with brute force protection and session management."""
    authenticated = session.get('is_admin', False)
    client_ip = get_real_client_ip()
    now = time.time()

    # Check if client IP is currently in temporary lockout
    ip_record = admin_failed_attempts.get(client_ip, {'count': 0, 'locked_until': 0})
    if ip_record['locked_until'] > now:
        remaining_secs = int(ip_record['locked_until'] - now)
        return render_template(
            'admin.html',
            error=f"Tài khoản bị tạm khóa do nhập sai quá 5 lần. Vui lòng thử lại sau {remaining_secs} giây.",
            authenticated=False
        )

    if request.method == 'POST':
        pwd = request.form.get('password', '')
        if pwd and pwd == config.ADMIN_PASSWORD:
            session['is_admin'] = True
            authenticated = True
            admin_failed_attempts.pop(client_ip, None)  # Reset failed count on success
        else:
            ip_record['count'] += 1
            if ip_record['count'] >= 5:
                ip_record['locked_until'] = now + 900  # Lock out for 15 minutes
                admin_failed_attempts[client_ip] = ip_record
                return render_template(
                    'admin.html',
                    error="Đăng nhập sai quá 5 lần! Hệ thống tạm khóa truy cập trong 15 phút.",
                    authenticated=False
                )
            else:
                admin_failed_attempts[client_ip] = ip_record
                attempts_left = 5 - ip_record['count']
                return render_template(
                    'admin.html',
                    error=f"Mật khẩu không chính xác! Còn lại {attempts_left} lần thử.",
                    authenticated=False
                )

    if not authenticated:
        return render_template('admin.html', authenticated=False)

    orders = database.get_all_orders()
    stats = database.get_sales_stats()
    file_path, file_name = get_distributable_file()
    file_status = {
        'exists': os.path.exists(file_path),
        'name': file_name,
        'size_mb': f"{os.path.getsize(file_path) / (1024 * 1024):.2f}" if os.path.exists(file_path) else "0.00"
    }

    return render_template(
        'admin.html',
        authenticated=True,
        orders=orders,
        stats=stats,
        file_status=file_status,
        csv_filename='orders.csv'
    )

@app.route('/admin/logout')
def admin_logout():
    session.pop('is_admin', None)
    return redirect(url_for('admin_dashboard'))

@app.route('/api/export-csv')
def export_csv():
    """Download the orders.csv file directly (Admin authentication required)."""
    if not session.get('is_admin', False):
        return redirect(url_for('admin_dashboard'))
    csv_path = database.CSV_FILE
    if not os.path.exists(csv_path):
        database.init_db()
    return send_file(csv_path, as_attachment=True, download_name="AutoLyrics_Orders.csv")

@app.route('/api/stats')
def api_stats():
    """Sales stats API (Admin authentication required)."""
    if not session.get('is_admin', False):
        return jsonify({'error': 'Unauthorized'}), 403
    return jsonify(database.get_sales_stats())

@app.route('/api/admin/generate-key', methods=['POST'])
def admin_generate_key():
    """Admin tool to generate a license key on demand using make_key.py and save to database."""
    if not session.get('is_admin', False):
        return jsonify({'success': False, 'error': 'Unauthorized'}), 403
    data = request.get_json() or request.form
    identifier = data.get('identifier', '').strip()
    if not identifier:
        return jsonify({'success': False, 'error': 'Vui lòng nhập Email hoặc Tên người dùng.'}), 400

    key = make_key.generate_license(identifier)

    # Check if an order already exists for this email
    existing_orders = database.get_orders_by_email(identifier)
    if existing_orders:
        order = existing_orders[0]
        order_id = order['order_id']
        download_token = order['download_token']
    else:
        # Save a new completed order record in SQLite and orders.csv!
        order_id = f"ADM-{datetime.now().strftime('%Y%m%d%H%M')}-{random.randint(100, 999)}"
        download_token = str(uuid.uuid4())
        client_ip = request.remote_addr or '127.0.0.1'

        order_data = {
            'order_id': order_id,
            'customer_name': 'Admin License',
            'customer_email': identifier,
            'base_price': 2.99,
            'tip_amount': 0.0,
            'total_amount': 2.99,
            'payment_method': 'Admin Direct (make_key.py)',
            'payment_status': 'COMPLETED',
            'license_key': key,
            'download_token': download_token,
            'ip_address': client_ip
        }
        database.save_order(order_data)

    return jsonify({
        'success': True,
        'identifier': identifier,
        'license_key': key,
        'order_id': order_id,
        'download_token': download_token
    })


# ============================================================================
# Main Entry Point
# ============================================================================

if __name__ == '__main__':
    file_path, file_name = get_distributable_file()
    print(f">> AutoLyrics Framework running at http://localhost:{config.SERVER_PORT}")
    print(f">> Distributable App: {file_name} ({os.path.getsize(file_path) / (1024*1024):.1f} MB)")
    print(f">> Database: {database.DB_FILE}")
    print(f">> CSV Log: {database.CSV_FILE}")
    print(f">> Admin URL: http://localhost:{config.SERVER_PORT}/admin (Password: {config.ADMIN_PASSWORD})")
    app.run(host=config.SERVER_HOST, port=config.SERVER_PORT, debug=True)
