from flask import Blueprint, render_template, redirect, url_for, flash, request, abort, current_app
from flask_login import login_user, logout_user, current_user, login_required
from app import login
from app.db import get_db_controller
from app.models import User
from app.utils import generate_unique_sub_name, generate_unique_code
from werkzeug.security import generate_password_hash, check_password_hash

bp = Blueprint('main', __name__)

@login.user_loader
def load_user(user_id):
    return User.get(user_id)

@bp.route('/')
def index():
    return render_template('index.html')

# --- Authentication ---
@bp.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('main.dashboard'))
    
    if request.method == 'POST':
        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        
        db = get_db_controller()
        if db.get_user_by_username(username) or db.get_user_by_email(email):
            flash('Username or Email already exists.')
            return redirect(url_for('main.register'))
        
        user_id = db.create_user(username, email, generate_password_hash(password))
        user = User.get(user_id)
        login_user(user)
        return redirect(url_for('main.dashboard'))
        
    return render_template('auth.html', mode='register')

@bp.route('/login', methods=['GET', 'POST'])
def login_view():
    if current_user.is_authenticated:
        return redirect(url_for('main.dashboard'))
        
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        
        db = get_db_controller()
        user_row = db.get_user_by_username(username)
        
        if user_row and check_password_hash(user_row['password_hash'], password):
            user = User.get(user_row['id'])
            login_user(user)
            next_page = request.args.get('next')
            return redirect(next_page or url_for('main.dashboard'))
        
        flash('Invalid username or password')
        return redirect(url_for('main.login_view'))
        
    return render_template('auth.html', mode='login')

@bp.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('main.index'))

# --- Dashboard ---
@bp.route('/dashboard')
@login_required
def dashboard():
    db = get_db_controller()
    subs = db.get_subscriptions_by_user(current_user.id)
    return render_template('dashboard.html', subscriptions=subs, stripe_pk=current_app.config['STRIPE_PUBLIC_KEY'])

@bp.route('/edit_subscription/<int:sub_id>', methods=['POST'])
@login_required
def edit_subscription(sub_id):
    # Logic to edit sub name or code
    new_name = request.form.get('sub_name')
    new_code = request.form.get('unique_code')
    
    db = get_db_controller()
    # Verify ownership
    # We need to fetch sub first to check owner, but get_subscriptions_by_user returns all.
    # Ideally should have get_sub_by_id. 
    # For now, simplistic check:
    subs = db.get_subscriptions_by_user(current_user.id)
    target_sub = next((s for s in subs if s['id'] == sub_id), None)
    
    if not target_sub:
        abort(403)
        
    # Check uniqueness if changed
    if new_name != target_sub['sub_name'] and db.check_sub_name_exists(new_name):
        flash('Subscription name already taken.')
        return redirect(url_for('main.dashboard'))
    
    if new_code != target_sub['unique_code'] and db.check_code_exists(new_code):
        flash('Code already taken.')
        return redirect(url_for('main.dashboard'))
        
    db.update_sub_name_code(sub_id, new_name, new_code)
    flash('Updated successfully.')
    return redirect(url_for('main.dashboard'))

# --- Admin ---
@bp.route('/admin')
@login_required
def admin():
    if not current_user.is_admin:
        abort(403)
    db = get_db_controller()
    users = db.get_all_users()
    subs = db.get_all_subscriptions()
    return render_template('admin.html', users=users, subscriptions=subs)

@bp.route('/admin/reset_password/<int:user_id>', methods=['POST'])
@login_required
def admin_reset_pw(user_id):
    if not current_user.is_admin:
        abort(403)
    new_pw = request.form['new_password']
    db = get_db_controller()
    db.update_password(user_id, generate_password_hash(new_pw))
    flash('Password reset.')
    return redirect(url_for('main.admin'))

@bp.route('/admin/reset_sub_code/<int:sub_id>', methods=['POST'])
@login_required
def admin_reset_code(sub_id):
    if not current_user.is_admin:
        abort(403)
    db = get_db_controller()
    # Need to fetch sub to keep name? Or just generate new code?
    # Simple query to get sub details is missing in controller for ID, but we can do a direct query or add helper.
    # The requirement: "reset subscription names and codes either manually or autogenerated".
    # Let's implementation autogen reset.
    new_code = generate_unique_code()
    
    # We need to execute a query. Controller has update_sub_name_code, but we need current name.
    # Let's add a quick helper or just use raw sql here if wrapper is too strict?
    # Better: Add get_sub_by_id to controller.
    # For now, I'll assume I can just update the code and keep name same? Logic is tricky without fetching.
    # I'll rely on the dashboard view for name.
    # Let's just create a raw query here using db.get_db() for speed or update controller later.
    # Actually, let's update controller in valid separate step if needed. 
    # For now, I'll disable this specific route until I fix controller or use a hack.
    # Actually, I can just use `db.get_db().execute(...)` directly since I have access to DbController instance?
    # Yes, DbController exposes get_db().
    
    # row = db.get_db().execute('SELECT sub_name FROM subscriptions WHERE id = ?', (sub_id,)).fetchone()
    # db.update_sub_name_code(sub_id, row['sub_name'], new_code)
    
    con = db.get_db()
    row = con.execute('SELECT sub_name FROM subscriptions WHERE id = ?', (sub_id,)).fetchone()
    if row:
        db.update_sub_name_code(sub_id, row['sub_name'], new_code)
        flash('Code reset.')
    
    return redirect(url_for('main.admin'))
