import os
import re
from datetime import datetime, date
from functools import wraps

from flask import (
    Flask, render_template, request, redirect, url_for, flash, abort,
    send_from_directory
)
from flask_sqlalchemy import SQLAlchemy
from flask_login import (
    LoginManager, UserMixin, login_user, logout_user, login_required,
    current_user
)
from werkzeug.security import generate_password_hash, check_password_hash
from werkzeug.utils import secure_filename

BASE_DIR = os.path.abspath(os.path.dirname(__file__))
os.makedirs(os.path.join(BASE_DIR, 'instance'), exist_ok=True)
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'static', 'uploads')
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'pdf'}

app = Flask(__name__)
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'change-this-secret-key-for-production')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
    'DATABASE_URL',
    'sqlite:///' + os.path.join(BASE_DIR, 'instance', 'apss_local.db')
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024

# SQLite compatibility if DATABASE_URL starts with postgres:// from some hosts
if app.config['SQLALCHEMY_DATABASE_URI'].startswith('postgres://'):
    app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_URI'].replace('postgres://', 'postgresql://', 1)
if app.config['SQLALCHEMY_DATABASE_URI'].startswith('mysql://'):
    app.config['SQLALCHEMY_DATABASE_URI'] = app.config['SQLALCHEMY_DATABASE_URI'].replace('mysql://', 'mysql+pymysql://', 1)

db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message_category = 'warning'


# -----------------------------
# Utility helpers
# -----------------------------

def slugify(text):
    text = (text or '').strip().lower()
    text = re.sub(r'[^a-z0-9]+', '-', text)
    text = text.strip('-')
    return text or 'item'


def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def save_upload(file_obj):
    if not file_obj or file_obj.filename == '':
        return None
    if not allowed_file(file_obj.filename):
        flash('Only image and PDF uploads are allowed.', 'danger')
        return None
    os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
    filename = secure_filename(file_obj.filename)
    stamp = datetime.utcnow().strftime('%Y%m%d%H%M%S')
    filename = f'{stamp}_{filename}'
    path = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    file_obj.save(path)
    return f'uploads/{filename}'


def admin_required(view_func):
    @wraps(view_func)
    def wrapper(*args, **kwargs):
        if not current_user.is_authenticated or current_user.role != 'admin':
            abort(403)
        return view_func(*args, **kwargs)
    return wrapper


def log_action(action, entity_type=None, entity_id=None, details=None):
    try:
        entry = AuditLog(
            user_id=current_user.id if current_user.is_authenticated else None,
            action=action,
            entity_type=entity_type,
            entity_id=entity_id,
            details=details,
            ip_address=request.headers.get('X-Forwarded-For', request.remote_addr),
        )
        db.session.add(entry)
        db.session.commit()
    except Exception:
        db.session.rollback()


# -----------------------------
# Database models
# -----------------------------

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(120), nullable=False)
    email = db.Column(db.String(180), unique=True, nullable=False)
    password_hash = db.Column(db.String(255), nullable=False)
    role = db.Column(db.String(50), default='admin')
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)


class Program(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(200), nullable=False)
    slug = db.Column(db.String(220), unique=True, nullable=False)
    summary = db.Column(db.Text, nullable=False)
    icon = db.Column(db.String(60), default='dialogue')
    is_featured = db.Column(db.Boolean, default=True)
    sort_order = db.Column(db.Integer, default=0)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class Event(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(240), nullable=False)
    slug = db.Column(db.String(260), unique=True, nullable=False)
    event_date = db.Column(db.Date)
    location = db.Column(db.String(220))
    category = db.Column(db.String(120), default='Dialogue')
    summary = db.Column(db.Text, nullable=False)
    description = db.Column(db.Text, nullable=False)
    cover_image = db.Column(db.String(255))
    is_featured = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class Publication(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(240), nullable=False)
    slug = db.Column(db.String(260), unique=True, nullable=False)
    author = db.Column(db.String(180), default='APSS')
    publication_type = db.Column(db.String(120), default='Report')
    year = db.Column(db.Integer)
    summary = db.Column(db.Text, nullable=False)
    file_path = db.Column(db.String(255))
    is_featured = db.Column(db.Boolean, default=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class GalleryImage(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(220), nullable=False)
    category = db.Column(db.String(120), default='APSS Activity')
    caption = db.Column(db.Text)
    image_path = db.Column(db.String(255))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class Member(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    full_name = db.Column(db.String(180), nullable=False)
    email = db.Column(db.String(180), nullable=False)
    phone = db.Column(db.String(80))
    district = db.Column(db.String(120))
    profession = db.Column(db.String(160))
    membership_type = db.Column(db.String(120), default='General Member')
    message = db.Column(db.Text)
    status = db.Column(db.String(40), default='Pending')
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class ContactMessage(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(180), nullable=False)
    email = db.Column(db.String(180), nullable=False)
    subject = db.Column(db.String(220), nullable=False)
    message = db.Column(db.Text, nullable=False)
    status = db.Column(db.String(40), default='Unread')
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class NewsletterSubscriber(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(180), unique=True, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


class AuditLog(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    action = db.Column(db.String(120), nullable=False)
    entity_type = db.Column(db.String(80))
    entity_id = db.Column(db.Integer)
    details = db.Column(db.Text)
    ip_address = db.Column(db.String(120))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


# -----------------------------
# Seed data
# -----------------------------

MISSION = (
    'To advance the dialogue of politics and political science by comparing empirical phenomenon with scholarly insights '
    'into how societies work, by committing to support next generation politics and by shaping a mutually-understood society.'
)

CORE_OBJECTIVES = [
    'Scholarly research activities in politics and development, communicated domestically, regionally and internationally.',
    'High quality teaching and education about politics, government, economics, political economy, sociology, culture, history and social sciences.',
    'Public service through research dissemination and preparation of citizens for effective political participation.',
    'Exchange of ideas, experience, mutual understanding, equal rights and opportunities among young generations.',
    'Meaningful participation of young and new generations in society and decision-making processes.',
    'Policy influence for citizen welfare, democracy, human rights, diversity, active citizenship and solidarity.'
]

TIMELINE = [
    {'year': '2004', 'title': 'Peace opportunity and alternative development avenues sought', 'text': 'APSS supported culture of multiparty democracy, critical thinking and lasting peace dialogue among young political leaders and student activists.'},
    {'year': '2006', 'title': 'Constructive political expressions enhanced', 'text': 'Youth groups were oriented toward peaceful protest skills, pluralism and constructive facilitation of Nepal’s peace process.'},
    {'year': '2010', 'title': 'Constitution-making process facilitated', 'text': 'Federalism and constitution crafting discussions were supported with experts, youth leaders and democratic values dissemination.'},
    {'year': '2014', 'title': 'Federalism ideas suited to Nepal conferred', 'text': 'Knowledge support was provided to political parties and groups around local service delivery, accountable governance and federal design.'},
    {'year': '2017', 'title': 'New constitution realization and geopolitics discussed', 'text': 'Policy support and analysis were shared on federal governance, local government roles and Nepal’s diplomatic relations.'},
    {'year': '2019', 'title': 'Political institutions, legitimacy and freedom discussed', 'text': 'APSS observed emerging relationships among stability, legitimacy, internet, AI, liberty, nationalism and Nepal’s political development.'},
]

VALUES = [
    {'title': 'Political Independence', 'text': 'APSS should not speak or act in a single voice on political issues.'},
    {'title': 'Professionalism', 'text': 'Every member should act responsibly toward the APSS community.'},
    {'title': 'Respecting Diversity', 'text': 'Tolerance is indispensable for national and international communication, cooperation and unity.'},
    {'title': 'Knowledge', 'text': 'Knowledge creates power for critical thinking in politics and society.'},
]


def seed_default_data():
    os.makedirs(UPLOAD_FOLDER, exist_ok=True)

    if not User.query.filter_by(email='admin@apss.org.np').first():
        admin = User(name='APSS Admin', email='admin@apss.org.np', role='admin')
        admin.set_password('admin123')
        db.session.add(admin)

    if Program.query.count() == 0:
        programs = [
            ('Annual National Political Science Conference', 'A flagship annual forum for scholars, political actors, students and public-policy stakeholders to review democratic practice and political science learning.', 'conference'),
            ('APSS Journal of Current Affairs', 'A publication platform for political science, governance, federalism, foreign policy, democracy and public affairs analysis.', 'journal'),
            ('Democracy Dialogue', 'Structured dialogues that bring together political parties, government, academia, youth groups, civil society and international partners.', 'democracy'),
            ('Political Roundtable', 'Focused high-level discussions on constitutionalism, political stability, governance, public accountability and national development.', 'roundtable'),
            ('Newsletter Publication', 'Regular communication of APSS updates, events, publications and democratic learning resources to members and partners.', 'newsletter'),
            ('Youth Leadership and Federalism Learning', 'Capacity-building activities for young leaders, students and local chapters on democracy, federalism and non-violent political expression.', 'youth'),
        ]
        for idx, (title, summary, icon) in enumerate(programs, start=1):
            db.session.add(Program(
                title=title,
                slug=slugify(title),
                summary=summary,
                icon=icon,
                sort_order=idx,
                is_featured=True,
            ))

    if Event.query.count() == 0:
        events = [
            {
                'title': 'Nepal-India Relations in Changing Context',
                'event_date': date(2017, 7, 26),
                'location': 'Kathmandu, Nepal',
                'category': 'Foreign Policy Dialogue',
                'summary': 'A one-day seminar organized by APSS and CNAS, Tribhuvan University on Nepal–India relations, connectivity, border governance and democracy challenges.',
                'description': 'The seminar brought together senior political leaders, foreign policy experts, diplomats, academics, media and commentators to discuss Nepal–India relations in a changing regional and democratic context.',
                'featured': True,
                'cover_image': 'images/hero-seminar.jpg',
            },
            {
                'title': 'Education Workshop with Ministry of Education/Nepal',
                'event_date': date(2010, 1, 15),
                'location': 'Kathmandu, Nepal',
                'category': 'Policy Workshop',
                'summary': 'A workshop to consolidate stakeholder thinking around compulsory education and effective education service delivery in Nepal.',
                'description': 'APSS convened political leaders, experts, administrators, journalists, civil society and Constituent Assembly members to discuss education policy, service delivery and accountability.',
                'featured': True,
                'cover_image': 'images/meeting-room.jpg',
            },
            {
                'title': 'National Talk Program on Peace Process and Constitution Building',
                'event_date': date(2010, 6, 20),
                'location': 'Kathmandu, Nepal',
                'category': 'Peace and Constitution',
                'summary': 'A national talk program with political parties, civil society, analysts, media, youth and student leaders on Nepal’s peace process and constitution-building agenda.',
                'description': 'The dialogue reviewed the political environment, conditions for concluding the peace process, government formation, combatant integration and the responsibilities of political actors and civil society.',
                'featured': True,
                'cover_image': 'images/roundtable.jpg',
            },
            {
                'title': 'Interaction with German Constitutional Judge Dr. Sigfried Bross',
                'event_date': date(2009, 3, 15),
                'location': 'Kathmandu, Nepal',
                'category': 'Constitutional Dialogue',
                'summary': 'An academic interaction on constitutional court practice and German constitutional experience.',
                'description': 'The program supported Nepal’s constitution drafting process through comparative knowledge on constitutional courts, federal democratic practice and safeguarding citizens’ rights.',
                'featured': False,
                'cover_image': 'images/roundtable.jpg',
            },
            {
                'title': 'Regional Consultation on State Restructure and Federalism',
                'event_date': date(2009, 9, 1),
                'location': 'Ilam, Janakpur, Dolakha, Pokhara and Nepalgunj',
                'category': 'Federalism Consultation',
                'summary': 'Regional consultations with youth and student leaders on federalism, state restructuring, education, employment and local participation.',
                'description': 'APSS conducted consultative dialogues with youth leaders across regions to identify youth perspectives on federal design, natural resource sharing, education, employment and meaningful participation in decision making.',
                'featured': False,
                'cover_image': 'images/meeting-room.jpg',
            },
        ]
        for item in events:
            db.session.add(Event(
                title=item['title'],
                slug=slugify(item['title']),
                event_date=item['event_date'],
                location=item['location'],
                category=item['category'],
                summary=item['summary'],
                description=item['description'],
                cover_image=item.get('cover_image'),
                is_featured=item['featured'],
            ))

    if Publication.query.count() == 0:
        pubs = [
            ('APSS Journal of Current Affairs', 'APSS', 'Journal', 2026, 'A placeholder entry for APSS journal publication. Upload PDF from Admin > Publications when available.', True),
            ('Democracy Dialogue Newsletter', 'APSS', 'Newsletter', 2026, 'A sample newsletter item for APSS updates, activities, policy discussions and member information.', True),
            ('Strategic Action Plan 2007-2011', 'APSS', 'Strategic Report', 2011, 'Historical strategic action plan developed after consultations with youth, students, experts and stakeholders.', False),
            ('Youth Participation in Constitution Writing Process', 'APSS', 'Project Report', 2010, 'A historical project report placeholder related to youth participation, federalism and constitution-building dialogue.', False),
        ]
        for title, author, ptype, year, summary, featured in pubs:
            db.session.add(Publication(
                title=title,
                slug=slugify(title),
                author=author,
                publication_type=ptype,
                year=year,
                summary=summary,
                is_featured=featured,
            ))

    if GalleryImage.query.count() == 0:
        gallery = [
            ('Political Roundtable', 'Roundtable', 'APSS dialogue session with political actors, experts and members.', 'images/roundtable.jpg'),
            ('Policy Dialogue Meeting', 'Policy Dialogue', 'APSS consultation and roundtable-style discussion with experts and members.', 'images/meeting-room.jpg'),
            ('Nepal-India Relations Seminar', 'International Dialogue', 'Seminar and public policy exchange on Nepal-India relations in a changing context.', 'images/hero-seminar.jpg'),
            ('Youth Federalism Consultation', 'Youth Program', 'Youth leaders participating in federalism and democratic learning activities.', 'images/roundtable.jpg'),
        ]
        for title, category, caption, image_path in gallery:
            db.session.add(GalleryImage(title=title, category=category, caption=caption, image_path=image_path))

    db.session.commit()


# -----------------------------
# Context processors
# -----------------------------

@app.context_processor
def inject_common():
    return {
        'now': datetime.utcnow(),
        'MISSION': MISSION,
        'CORE_OBJECTIVES': CORE_OBJECTIVES,
        'TIMELINE': TIMELINE,
        'VALUES': VALUES,
    }


# -----------------------------
# Public routes
# -----------------------------

@app.route('/')
def home():
    featured_programs = Program.query.filter_by(is_featured=True).order_by(Program.sort_order.asc()).limit(6).all()
    featured_events = Event.query.filter_by(is_featured=True).order_by(Event.event_date.desc()).limit(3).all()
    featured_publications = Publication.query.filter_by(is_featured=True).order_by(Publication.year.desc()).limit(3).all()
    return render_template('public/home.html', featured_programs=featured_programs, featured_events=featured_events, featured_publications=featured_publications)


@app.route('/about')
def about():
    return render_template('public/about.html')


@app.route('/programs')
def programs():
    items = Program.query.order_by(Program.sort_order.asc(), Program.title.asc()).all()
    return render_template('public/programs.html', programs=items)


@app.route('/events')
def events():
    category = request.args.get('category', '').strip()
    q = request.args.get('q', '').strip()
    query = Event.query
    if category:
        query = query.filter(Event.category == category)
    if q:
        like = f'%{q}%'
        query = query.filter(db.or_(Event.title.ilike(like), Event.summary.ilike(like), Event.description.ilike(like)))
    items = query.order_by(Event.event_date.desc().nullslast(), Event.created_at.desc()).all()
    categories = [row[0] for row in db.session.query(Event.category).distinct().order_by(Event.category.asc()).all()]
    return render_template('public/events.html', events=items, categories=categories, selected_category=category, q=q)


@app.route('/events/<slug>')
def event_detail(slug):
    item = Event.query.filter_by(slug=slug).first_or_404()
    return render_template('public/event_detail.html', event=item)


@app.route('/publications')
def publications():
    ptype = request.args.get('type', '').strip()
    q = request.args.get('q', '').strip()
    query = Publication.query
    if ptype:
        query = query.filter(Publication.publication_type == ptype)
    if q:
        like = f'%{q}%'
        query = query.filter(db.or_(Publication.title.ilike(like), Publication.summary.ilike(like), Publication.author.ilike(like)))
    items = query.order_by(Publication.year.desc().nullslast(), Publication.created_at.desc()).all()
    types = [row[0] for row in db.session.query(Publication.publication_type).distinct().order_by(Publication.publication_type.asc()).all()]
    return render_template('public/publications.html', publications=items, types=types, selected_type=ptype, q=q)


@app.route('/timeline')
def timeline():
    return render_template('public/timeline.html')


@app.route('/gallery')
def gallery():
    items = GalleryImage.query.order_by(GalleryImage.created_at.desc()).all()
    return render_template('public/gallery.html', gallery=items)


@app.route('/membership', methods=['GET', 'POST'])
def membership():
    if request.method == 'POST':
        member = Member(
            full_name=request.form.get('full_name', '').strip(),
            email=request.form.get('email', '').strip(),
            phone=request.form.get('phone', '').strip(),
            district=request.form.get('district', '').strip(),
            profession=request.form.get('profession', '').strip(),
            membership_type=request.form.get('membership_type', 'General Member').strip(),
            message=request.form.get('message', '').strip(),
        )
        if not member.full_name or not member.email:
            flash('Full name and email are required.', 'danger')
            return redirect(url_for('membership'))
        db.session.add(member)
        db.session.commit()
        log_action('membership_application_submitted', 'Member', member.id, member.email)
        flash('Your membership application has been submitted. APSS admin can review it from the dashboard.', 'success')
        return redirect(url_for('membership'))
    return render_template('public/membership.html')


@app.route('/contact', methods=['GET', 'POST'])
def contact():
    if request.method == 'POST':
        msg = ContactMessage(
            name=request.form.get('name', '').strip(),
            email=request.form.get('email', '').strip(),
            subject=request.form.get('subject', '').strip(),
            message=request.form.get('message', '').strip(),
        )
        if not msg.name or not msg.email or not msg.subject or not msg.message:
            flash('Please complete all required fields.', 'danger')
            return redirect(url_for('contact'))
        db.session.add(msg)
        db.session.commit()
        log_action('contact_message_submitted', 'ContactMessage', msg.id, msg.email)
        flash('Thank you. Your message has been received.', 'success')
        return redirect(url_for('contact'))
    return render_template('public/contact.html')


@app.route('/subscribe', methods=['POST'])
def subscribe():
    email = request.form.get('email', '').strip()
    if not email:
        flash('Please provide an email address.', 'warning')
        return redirect(request.referrer or url_for('home'))
    if NewsletterSubscriber.query.filter_by(email=email).first():
        flash('This email is already subscribed.', 'info')
        return redirect(request.referrer or url_for('home'))
    db.session.add(NewsletterSubscriber(email=email))
    db.session.commit()
    flash('Newsletter subscription saved.', 'success')
    return redirect(request.referrer or url_for('home'))


@app.route('/uploads/<path:filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename)


# -----------------------------
# Auth routes
# -----------------------------

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('admin_dashboard'))
    if request.method == 'POST':
        email = request.form.get('email', '').strip().lower()
        password = request.form.get('password', '')
        user = User.query.filter_by(email=email).first()
        if user and user.check_password(password):
            login_user(user)
            log_action('login', 'User', user.id, user.email)
            flash('Welcome back.', 'success')
            return redirect(url_for('admin_dashboard'))
        flash('Invalid email or password.', 'danger')
    return render_template('public/login.html')


@app.route('/logout')
@login_required
def logout():
    log_action('logout', 'User', current_user.id, current_user.email)
    logout_user()
    flash('You have been logged out.', 'info')
    return redirect(url_for('home'))


# -----------------------------
# Admin routes
# -----------------------------

@app.route('/admin')
@login_required
@admin_required
def admin_dashboard():
    stats = {
        'programs': Program.query.count(),
        'events': Event.query.count(),
        'publications': Publication.query.count(),
        'members_pending': Member.query.filter_by(status='Pending').count(),
        'messages_unread': ContactMessage.query.filter_by(status='Unread').count(),
        'subscribers': NewsletterSubscriber.query.count(),
    }
    recent_members = Member.query.order_by(Member.created_at.desc()).limit(5).all()
    recent_messages = ContactMessage.query.order_by(ContactMessage.created_at.desc()).limit(5).all()
    return render_template('admin/dashboard.html', stats=stats, recent_members=recent_members, recent_messages=recent_messages)


@app.route('/admin/programs')
@login_required
@admin_required
def admin_programs():
    items = Program.query.order_by(Program.sort_order.asc(), Program.title.asc()).all()
    return render_template('admin/programs.html', programs=items)


@app.route('/admin/programs/new', methods=['GET', 'POST'])
@app.route('/admin/programs/<int:item_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def admin_program_form(item_id=None):
    item = Program.query.get(item_id) if item_id else None
    if request.method == 'POST':
        if item is None:
            item = Program(slug='temp')
            db.session.add(item)
        item.title = request.form.get('title', '').strip()
        item.slug = slugify(request.form.get('slug') or item.title)
        item.summary = request.form.get('summary', '').strip()
        item.icon = request.form.get('icon', 'dialogue').strip()
        item.sort_order = int(request.form.get('sort_order') or 0)
        item.is_featured = bool(request.form.get('is_featured'))
        db.session.commit()
        log_action('save_program', 'Program', item.id, item.title)
        flash('Program saved.', 'success')
        return redirect(url_for('admin_programs'))
    return render_template('admin/program_form.html', item=item)


@app.route('/admin/programs/<int:item_id>/delete', methods=['POST'])
@login_required
@admin_required
def admin_program_delete(item_id):
    item = Program.query.get_or_404(item_id)
    db.session.delete(item)
    db.session.commit()
    log_action('delete_program', 'Program', item_id, item.title)
    flash('Program deleted.', 'info')
    return redirect(url_for('admin_programs'))


@app.route('/admin/events')
@login_required
@admin_required
def admin_events():
    items = Event.query.order_by(Event.event_date.desc().nullslast(), Event.created_at.desc()).all()
    return render_template('admin/events.html', events=items)


@app.route('/admin/events/new', methods=['GET', 'POST'])
@app.route('/admin/events/<int:item_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def admin_event_form(item_id=None):
    item = Event.query.get(item_id) if item_id else None
    if request.method == 'POST':
        if item is None:
            item = Event(slug='temp', summary='', description='')
            db.session.add(item)
        item.title = request.form.get('title', '').strip()
        item.slug = slugify(request.form.get('slug') or item.title)
        date_text = request.form.get('event_date', '').strip()
        item.event_date = datetime.strptime(date_text, '%Y-%m-%d').date() if date_text else None
        item.location = request.form.get('location', '').strip()
        item.category = request.form.get('category', '').strip() or 'Dialogue'
        item.summary = request.form.get('summary', '').strip()
        item.description = request.form.get('description', '').strip()
        item.is_featured = bool(request.form.get('is_featured'))
        upload_path = save_upload(request.files.get('cover_image'))
        if upload_path:
            item.cover_image = upload_path
        db.session.commit()
        log_action('save_event', 'Event', item.id, item.title)
        flash('Event saved.', 'success')
        return redirect(url_for('admin_events'))
    return render_template('admin/event_form.html', item=item)


@app.route('/admin/events/<int:item_id>/delete', methods=['POST'])
@login_required
@admin_required
def admin_event_delete(item_id):
    item = Event.query.get_or_404(item_id)
    db.session.delete(item)
    db.session.commit()
    log_action('delete_event', 'Event', item_id, item.title)
    flash('Event deleted.', 'info')
    return redirect(url_for('admin_events'))


@app.route('/admin/publications')
@login_required
@admin_required
def admin_publications():
    items = Publication.query.order_by(Publication.year.desc().nullslast(), Publication.created_at.desc()).all()
    return render_template('admin/publications.html', publications=items)


@app.route('/admin/publications/new', methods=['GET', 'POST'])
@app.route('/admin/publications/<int:item_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def admin_publication_form(item_id=None):
    item = Publication.query.get(item_id) if item_id else None
    if request.method == 'POST':
        if item is None:
            item = Publication(slug='temp', summary='')
            db.session.add(item)
        item.title = request.form.get('title', '').strip()
        item.slug = slugify(request.form.get('slug') or item.title)
        item.author = request.form.get('author', '').strip() or 'APSS'
        item.publication_type = request.form.get('publication_type', '').strip() or 'Report'
        year_text = request.form.get('year', '').strip()
        item.year = int(year_text) if year_text else None
        item.summary = request.form.get('summary', '').strip()
        item.is_featured = bool(request.form.get('is_featured'))
        upload_path = save_upload(request.files.get('file_path'))
        if upload_path:
            item.file_path = upload_path
        db.session.commit()
        log_action('save_publication', 'Publication', item.id, item.title)
        flash('Publication saved.', 'success')
        return redirect(url_for('admin_publications'))
    return render_template('admin/publication_form.html', item=item)


@app.route('/admin/publications/<int:item_id>/delete', methods=['POST'])
@login_required
@admin_required
def admin_publication_delete(item_id):
    item = Publication.query.get_or_404(item_id)
    db.session.delete(item)
    db.session.commit()
    log_action('delete_publication', 'Publication', item_id, item.title)
    flash('Publication deleted.', 'info')
    return redirect(url_for('admin_publications'))


@app.route('/admin/gallery')
@login_required
@admin_required
def admin_gallery():
    items = GalleryImage.query.order_by(GalleryImage.created_at.desc()).all()
    return render_template('admin/gallery.html', gallery=items)


@app.route('/admin/gallery/new', methods=['GET', 'POST'])
@app.route('/admin/gallery/<int:item_id>/edit', methods=['GET', 'POST'])
@login_required
@admin_required
def admin_gallery_form(item_id=None):
    item = GalleryImage.query.get(item_id) if item_id else None
    if request.method == 'POST':
        if item is None:
            item = GalleryImage()
            db.session.add(item)
        item.title = request.form.get('title', '').strip()
        item.category = request.form.get('category', '').strip() or 'APSS Activity'
        item.caption = request.form.get('caption', '').strip()
        upload_path = save_upload(request.files.get('image_path'))
        if upload_path:
            item.image_path = upload_path
        db.session.commit()
        log_action('save_gallery_image', 'GalleryImage', item.id, item.title)
        flash('Gallery item saved.', 'success')
        return redirect(url_for('admin_gallery'))
    return render_template('admin/gallery_form.html', item=item)


@app.route('/admin/gallery/<int:item_id>/delete', methods=['POST'])
@login_required
@admin_required
def admin_gallery_delete(item_id):
    item = GalleryImage.query.get_or_404(item_id)
    db.session.delete(item)
    db.session.commit()
    log_action('delete_gallery_image', 'GalleryImage', item_id, item.title)
    flash('Gallery item deleted.', 'info')
    return redirect(url_for('admin_gallery'))


@app.route('/admin/members')
@login_required
@admin_required
def admin_members():
    status = request.args.get('status', '').strip()
    query = Member.query
    if status:
        query = query.filter_by(status=status)
    items = query.order_by(Member.created_at.desc()).all()
    return render_template('admin/members.html', members=items, selected_status=status)


@app.route('/admin/members/<int:item_id>/status', methods=['POST'])
@login_required
@admin_required
def admin_member_status(item_id):
    item = Member.query.get_or_404(item_id)
    new_status = request.form.get('status', 'Pending')
    if new_status not in ['Pending', 'Approved', 'Rejected', 'Inactive']:
        new_status = 'Pending'
    item.status = new_status
    db.session.commit()
    log_action('update_member_status', 'Member', item.id, new_status)
    flash('Member status updated.', 'success')
    return redirect(url_for('admin_members'))


@app.route('/admin/messages')
@login_required
@admin_required
def admin_messages():
    items = ContactMessage.query.order_by(ContactMessage.created_at.desc()).all()
    return render_template('admin/messages.html', messages=items)


@app.route('/admin/messages/<int:item_id>/toggle', methods=['POST'])
@login_required
@admin_required
def admin_message_toggle(item_id):
    item = ContactMessage.query.get_or_404(item_id)
    item.status = 'Read' if item.status == 'Unread' else 'Unread'
    db.session.commit()
    log_action('toggle_message_status', 'ContactMessage', item.id, item.status)
    flash('Message status updated.', 'success')
    return redirect(url_for('admin_messages'))


@app.route('/admin/subscribers')
@login_required
@admin_required
def admin_subscribers():
    items = NewsletterSubscriber.query.order_by(NewsletterSubscriber.created_at.desc()).all()
    return render_template('admin/subscribers.html', subscribers=items)


@app.route('/admin/audit-logs')
@login_required
@admin_required
def admin_audit_logs():
    items = AuditLog.query.order_by(AuditLog.created_at.desc()).limit(100).all()
    return render_template('admin/audit_logs.html', logs=items)


@app.errorhandler(403)
def forbidden(error):
    return render_template('public/error.html', code=403, message='You do not have permission to access this page.'), 403


@app.errorhandler(404)
def not_found(error):
    return render_template('public/error.html', code=404, message='The requested page was not found.'), 404


# Initialize local database automatically for easy testing
with app.app_context():
    db.create_all()
    seed_default_data()


if __name__ == '__main__':
    app.run(debug=True)
