import os
from flask import Flask, render_template, redirect, url_for, flash, request, abort
from flask_login import (
    LoginManager, login_user, logout_user, login_required, current_user
)
from models import db, User, Category, Contact

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key-change-in-production')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///contacts.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'


@login_manager.user_loader
def load_user(user_id):
    return db.session.get(User, int(user_id))


# ── Database helpers ────────────────────────────────────────────────────

def get_user_categories():
    return Category.query.filter_by(user_id=current_user.id).order_by(Category.name).all()


def get_category_or_404(category_id):
    cat = db.session.get(Category, category_id)
    if cat is None or cat.user_id != current_user.id:
        abort(404)
    return cat


def get_contact_or_404(contact_id):
    c = db.session.get(Contact, contact_id)
    if c is None or c.user_id != current_user.id:
        abort(404)
    return c


# ── Context processor (sidebar data for all templates) ──────────────────

@app.context_processor
def inject_sidebar_data():
    if current_user.is_authenticated:
        cats = get_user_categories()
        return dict(categories=cats)
    return dict(categories=[])


# ── Auth routes ─────────────────────────────────────────────────────────

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))

    if request.method == 'POST':
        username = request.form['username'].strip()
        password = request.form['password']
        confirm = request.form.get('confirm_password', '')

        if not username or not password:
            flash('Username and password are required.', 'error')
        elif password != confirm:
            flash('Passwords do not match.', 'error')
        elif User.query.filter_by(username=username).first():
            flash('Username already taken.', 'error')
        else:
            user = User(username=username)
            user.set_password(password)
            db.session.add(user)
            db.session.commit()
            flash('Registration successful! Please log in.', 'success')
            return redirect(url_for('login'))

    return render_template('register.html')


@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))

    if request.method == 'POST':
        username = request.form['username'].strip()
        password = request.form['password']
        user = User.query.filter_by(username=username).first()

        if user is None or not user.check_password(password):
            flash('Invalid username or password.', 'error')
        else:
            login_user(user)
            next_page = request.args.get('next')
            return redirect(next_page or url_for('index'))

    return render_template('login.html')


@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('login'))


# ── Category routes ─────────────────────────────────────────────────────

@app.route('/')
@login_required
def index():
    categories = get_user_categories()
    return render_template('index.html', categories=categories)


@app.route('/category/new', methods=['GET', 'POST'])
@login_required
def new_category():
    if request.method == 'POST':
        name = request.form['name'].strip()
        if not name:
            flash('Category name is required.', 'error')
        else:
            cat = Category(name=name, user_id=current_user.id)
            db.session.add(cat)
            db.session.commit()
            flash(f'Category "{name}" created.', 'success')
            return redirect(url_for('index'))

    return render_template('category_form.html', category=None)


@app.route('/category/<int:category_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_category(category_id):
    cat = get_category_or_404(category_id)

    if request.method == 'POST':
        name = request.form['name'].strip()
        if not name:
            flash('Category name is required.', 'error')
        else:
            cat.name = name
            db.session.commit()
            flash('Category updated.', 'success')
            return redirect(url_for('index'))

    return render_template('category_form.html', category=cat)


@app.route('/category/<int:category_id>/delete', methods=['POST'])
@login_required
def delete_category(category_id):
    cat = get_category_or_404(category_id)
    db.session.delete(cat)
    db.session.commit()
    flash('Category deleted.', 'success')
    return redirect(url_for('index'))


# ── Contact routes ──────────────────────────────────────────────────────

@app.route('/category/<int:category_id>')
@login_required
def view_category(category_id):
    cat = get_category_or_404(category_id)
    contacts = Contact.query.filter_by(
        category_id=cat.id, user_id=current_user.id
    ).order_by(Contact.surname, Contact.name).all()
    return render_template('category.html', category=cat, contacts=contacts)


@app.route('/search')
@login_required
def search():
    q = request.args.get('q', '').strip()
    results = []
    if q:
        like = f'%{q}%'
        results = Contact.query.filter(
            Contact.user_id == current_user.id,
            db.or_(
                Contact.name.ilike(like),
                Contact.surname.ilike(like),
                Contact.address.ilike(like),
                Contact.phone.ilike(like),
                Contact.mobile.ilike(like),
                Contact.notes.ilike(like),
                Contact.property_id.ilike(like),
                Contact.map_location.ilike(like),
            )
        ).order_by(Contact.surname, Contact.name).all()
    return render_template('search.html', query=q, results=results)


@app.route('/contact/new', methods=['GET', 'POST'])
@login_required
def new_contact_generic():
    all_cats = get_user_categories()
    if not all_cats:
        flash('Create a category first before adding contacts.', 'error')
        return redirect(url_for('index'))

    if request.method == 'POST':
        name = request.form['name'].strip()
        cat_id = request.form.get('category_id', type=int)
        cat = get_category_or_404(cat_id) if cat_id else None
        if not name or not cat:
            flash('Name and category are required.', 'error')
        else:
            contact = Contact(
                name=name,
                surname=request.form.get('surname', '').strip(),
                address=request.form.get('address', '').strip(),
                phone=request.form.get('phone', '').strip(),
                mobile=request.form.get('mobile', '').strip(),
                notes=request.form.get('notes', '').strip(),
                property_id=request.form.get('property_id', '').strip(),
                map_location=request.form.get('map_location', '').strip(),
                category_id=cat.id,
                user_id=current_user.id,
            )
            db.session.add(contact)
            db.session.commit()
            flash(f'Contact "{name}" added.', 'success')
            return redirect(url_for('view_category', category_id=cat.id))

    return render_template('contact_form.html', category=None, all_categories=all_cats)


@app.route('/category/<int:category_id>/contact/new', methods=['GET', 'POST'])
@login_required
def new_contact(category_id):
    cat = get_category_or_404(category_id)

    if request.method == 'POST':
        name = request.form['name'].strip()
        if not name:
            flash('Name is required.', 'error')
        else:
            contact = Contact(
                name=name,
                surname=request.form.get('surname', '').strip(),
                address=request.form.get('address', '').strip(),
                phone=request.form.get('phone', '').strip(),
                mobile=request.form.get('mobile', '').strip(),
                notes=request.form.get('notes', '').strip(),
                property_id=request.form.get('property_id', '').strip(),
                map_location=request.form.get('map_location', '').strip(),
                category_id=cat.id,
                user_id=current_user.id,
            )
            db.session.add(contact)
            db.session.commit()
            flash(f'Contact "{name}" added.', 'success')
            return redirect(url_for('view_category', category_id=cat.id))

    return render_template('contact_form.html', category=cat, contact=None)


@app.route('/contact/<int:contact_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_contact(contact_id):
    contact = get_contact_or_404(contact_id)

    if request.method == 'POST':
        name = request.form['name'].strip()
        if not name:
            flash('Name is required.', 'error')
        else:
            contact.name = name
            contact.surname = request.form.get('surname', '').strip()
            contact.address = request.form.get('address', '').strip()
            contact.phone = request.form.get('phone', '').strip()
            contact.mobile = request.form.get('mobile', '').strip()
            contact.notes = request.form.get('notes', '').strip()
            contact.property_id = request.form.get('property_id', '').strip()
            contact.map_location = request.form.get('map_location', '').strip()
            db.session.commit()
            flash('Contact updated.', 'success')
            return redirect(url_for(
                'view_category', category_id=contact.category_id
            ))

    return render_template(
        'contact_form.html', category=contact.category, contact=contact
    )


@app.route('/contact/<int:contact_id>/delete', methods=['POST'])
@login_required
def delete_contact(contact_id):
    contact = get_contact_or_404(contact_id)
    cat_id = contact.category_id
    db.session.delete(contact)
    db.session.commit()
    flash('Contact deleted.', 'success')
    return redirect(url_for('view_category', category_id=cat_id))


# ── Entry point ─────────────────────────────────────────────────────────

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
    app.run(debug=True, host='0.0.0.0', port=5000)
