# grassroots_app/app.py
"""
Simplified Grassroots Events Management System
- Minimal dependencies
- Low memory footprint
- Single process friendly
- Role-based event management
"""

import os
from datetime import datetime, timedelta
from functools import wraps
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session
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 dotenv import load_dotenv
import logging
import sys

# Setup basic logging to file
log_dir = os.path.dirname(os.path.abspath(__file__))
log_file = os.path.join(log_dir, 'flask_debug.log')

logging.basicConfig(
    filename=log_file,
    level=logging.DEBUG,
    format='%(asctime)s %(levelname)s: %(message)s'
)

logging.info("Flask app starting...")
logging.info(f"Current directory: {log_dir}")
logging.info(f"Python path: {sys.path}")

# Load environment variables
load_dotenv()

app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', 'dev-secret-key')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
    'DATABASE_URL',
    'mysql+pymysql://synzhico_dbsuser:dbSYNZ81zUpwdVV798@localhost:3306/synzhico_grassroots_prod_db'
)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ENGINE_OPTIONS'] = {
    'pool_recycle': 280,
    'pool_pre_ping': True,
    'pool_size': 5,          # Increase for production
    'max_overflow': 10,       # Allow more connections
    'pool_timeout': 30        # Connection timeout
}

MAX_SUGGESTED_VENUES = 8  # Number of venue suggestions to show
db = SQLAlchemy(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message = 'Please log in to access this page.'

# ============================================================
# MODELS
# ============================================================

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password_hash = db.Column(db.String(200), nullable=False)
    role = db.Column(db.String(50), default='user')  # super_admin, admin, organising_gro, user
    company_id = db.Column(db.Integer, db.ForeignKey('companies.id'))
    department = db.Column(db.String(100))  # Constituency name
    sub_division = db.Column(db.String(100))  # For finer filtering
    is_active = db.Column(db.Boolean, default=True)
    last_login_at = db.Column(db.DateTime, nullable=True)
    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)
    
    @property
    def constituency(self):
        return self.department  # Department stores constituency name

    @property
    def can_manage_events(self):
        return self.role in ['admin', 'super_admin', 'organising_gro']
    
    @property
    def is_organising_gro(self):
        return self.role == 'organising_gro'
    
    @property
    def is_admin(self):
        return self.role in ['admin', 'super_admin']
    
    @property
    def is_super_admin(self):
        return self.role == 'super_admin'
    

class Company(db.Model):
    __tablename__ = 'companies'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False)
    domain = db.Column(db.String(100), unique=True)
    is_active = db.Column(db.Boolean, default=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)

class Venue(db.Model):
    __tablename__ = 'venues'
    id = db.Column(db.Integer, primary_key=True)
    company_id = db.Column(db.Integer, db.ForeignKey('companies.id'), nullable=False)
    created_by_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    name = db.Column(db.String(255), nullable=False)
    address = db.Column(db.String(500))
    constituency = db.Column(db.String(100))
    sub_division = db.Column(db.String(100))
    capacity = db.Column(db.Integer, default=0)
    is_permanent = db.Column(db.Boolean, default=True)
    is_default = db.Column(db.Boolean, default=False)
    usage_count = db.Column(db.Integer, default=0)
    last_used_at = db.Column(db.DateTime)
    is_active = db.Column(db.Boolean, default=True)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    
    company = db.relationship('Company', backref='venues')
    created_by = db.relationship('User', foreign_keys=[created_by_id])
    
    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'address': self.address,
            'constituency': self.constituency,
            'sub_division': self.sub_division,
            'capacity': self.capacity,
            'is_permanent': self.is_permanent,
            'is_default': self.is_default,
            'usage_count': self.usage_count
        }

class GrassrootsEvent(db.Model):
    __tablename__ = 'grassroots_events'
    id = db.Column(db.Integer, primary_key=True)
    event_uid = db.Column(db.String(36), unique=True, nullable=False)
    company_id = db.Column(db.Integer, db.ForeignKey('companies.id'), nullable=False)
    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    event_date = db.Column(db.Date, nullable=False)
    start_time = db.Column(db.Time, nullable=False)
    end_time = db.Column(db.Time, nullable=False)
    venue = db.Column(db.String(255), nullable=False)
    constituency = db.Column(db.String(100))
    sub_division = db.Column(db.String(100))
    organising_gro = db.Column(db.String(100), nullable=False)
    organising_gro_contact = db.Column(db.String(50))
    organising_gro_email = db.Column(db.String(200))
    expected_participants = db.Column(db.Integer, default=0)
    gra_attending = db.Column(db.Boolean, default=False)
    gra_name = db.Column(db.String(200))
    gra_attending_confirmed = db.Column(db.Boolean, default=False)  # GRA confirmed attendance
    gra_start_time = db.Column(db.Time, nullable=True)  # GRA arrival time
    gra_end_time = db.Column(db.Time, nullable=True)    # GRA departure time
    status = db.Column(db.String(50), default='scheduled')
    created_by_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_by_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
    is_active = db.Column(db.Boolean, default=True)
    
    company = db.relationship('Company', backref='events')
    created_by = db.relationship('User', foreign_keys=[created_by_id])
    updated_by = db.relationship('User', foreign_keys=[updated_by_id])
   
 
    def can_view(self, user):
        """Check if user can view this event"""
        if not user.is_authenticated:
            return False
        if user.is_admin or user.is_super_admin:
            return True
        # Organising GRO can view their own events
        if self.organising_gro == user.username:
            return True
        # Users can view events in their constituency
        if user.constituency and self.constituency == user.constituency:
            return True
        return False
    
    def can_edit(self, user):
        """Check if user can edit this event"""
        if not user.is_authenticated:
            return False
        if user.is_admin or user.is_super_admin:
            return True
        # Only organising GRO can edit their own events
        return self.organising_gro == user.username
    
    def can_delete(self, user):
        """Check if user can delete this event"""
        return self.can_edit(user)  # Same as edit permission
    
    def to_dict(self):
        return {
            'id': self.id,
            'name': self.name,
            'description': self.description,
            'event_date': self.event_date.isoformat() if self.event_date else None,
            'start_time': self.start_time.strftime('%H:%M') if self.start_time else None,
            'end_time': self.end_time.strftime('%H:%M') if self.end_time else None,
            'venue': self.venue,
            'constituency': self.constituency,
            'organising_gro': self.organising_gro,
            'expected_participants': self.expected_participants,
            'status': self.status,
            'can_edit': self.can_edit(current_user) if hasattr(current_user, 'id') else False
        }
    


# ============================================================
# AUTHENTICATION HELPERS
# ============================================================

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


def admin_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated or not current_user.is_admin:
            flash('Admin access required.', 'danger')
            return redirect(url_for('dashboard'))
        return f(*args, **kwargs)
    return decorated_function


def manager_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated or not current_user.can_manage_events:
            flash('You do not have permission to manage events.', 'danger')
            return redirect(url_for('dashboard'))
        return f(*args, **kwargs)
    return decorated_function


def get_suggested_venues_for_gro(user, limit=MAX_SUGGESTED_VENUES):
    """
    Get suggested venues for an organising_gro based on:
    1. Their permanent venues (sorted by usage count, max 8)
    2. Frequently used venues from their events (max 8)
    3. Shared venues they have explicitly added (max 8)
    Total suggestions will be limited to the specified limit (default 8)
    """
    suggested = []
    seen = set()
    
    # Priority 1: Permanent venues created by this GRO (top 8 by usage)
    permanent_venues = Venue.query.filter(
        Venue.company_id == user.company_id,
        Venue.created_by_id == user.id,
        Venue.is_active == True,
        Venue.is_permanent == True
    ).order_by(
        Venue.is_default.desc(),
        Venue.usage_count.desc(),
        Venue.name
    ).limit(limit).all()  # Limit to top 8
    
    for venue in permanent_venues:
        if venue.name not in seen and len(suggested) < limit:
            venue.is_shared = False
            suggested.append(venue)
            seen.add(venue.name)
    
    # If we already have enough suggestions, return early
    if len(suggested) >= limit:
        return suggested[:limit]
    
    # Priority 2: Frequently used venues from events (top venues by usage)
    frequent_from_events = db.session.query(
        GrassrootsEvent.venue,
        db.func.count(GrassrootsEvent.id).label('usage_count')
    ).filter(
        GrassrootsEvent.company_id == user.company_id,
        GrassrootsEvent.organising_gro == user.username,
        GrassrootsEvent.is_active == True
    ).group_by(GrassrootsEvent.venue).order_by(
        db.func.count(GrassrootsEvent.id).desc()
    ).limit(limit).all()  # Limit to top 8
    
    for venue_name, count in frequent_from_events:
        if venue_name not in seen and len(suggested) < limit:
            class TempVenue:
                def __init__(self, name, usage_count):
                    self.name = name
                    self.usage_count = usage_count
                    self.is_default = False
                    self.constituency = None
                    self.capacity = None
                    self.is_shared = False
                    self.is_permanent = False
            suggested.append(TempVenue(venue_name, count))
            seen.add(venue_name)
    
    # If we already have enough suggestions, return early
    if len(suggested) >= limit:
        return suggested[:limit]
    
    # Priority 3: Shared venues that user has explicitly added (top by usage)
    added_shared_venues = Venue.query.filter(
        Venue.company_id == user.company_id,
        Venue.created_by_id == user.id,
        Venue.is_active == True,
        Venue.is_permanent == True
    ).order_by(
        Venue.usage_count.desc(),
        Venue.name
    ).limit(limit).all()  # Limit to top 8
    
    for venue in added_shared_venues:
        if venue.name not in seen and len(suggested) < limit:
            venue.is_shared = True  # Mark as shared (adopted)
            suggested.append(venue)
            seen.add(venue.name)
    
    return suggested[:limit]  # Ensure we never return more than limit


def check_venue_conflict(venue, event_date, start_time, end_time, exclude_event_id=None):
    """
    Check if a venue is already booked for the given date and time slot.
    
    Args:
        venue: Venue name/location
        event_date: Date of the event
        start_time: Start time of the event
        end_time: End time of the event
        exclude_event_id: ID of event to exclude (for edit scenario)
    
    Returns:
        tuple: (has_conflict, conflicting_event)
    """
    # Build query to find conflicting events
    query = GrassrootsEvent.query.filter(
        GrassrootsEvent.venue == venue,
        GrassrootsEvent.event_date == event_date,
        GrassrootsEvent.is_active == True,
        GrassrootsEvent.status == 'scheduled'  # Only check scheduled events
    )
    
    # Exclude current event when editing
    if exclude_event_id:
        query = query.filter(GrassrootsEvent.id != exclude_event_id)
    
    # Check for time overlap
    for existing_event in query.all():
        # Check if time slots overlap
        if (start_time < existing_event.end_time and end_time > existing_event.start_time):
            return True, existing_event
    
    return False, None


def validate_event_times(event_date, start_time, end_time):
    """
    Validate that start time is before end time.
    
    Returns:
        tuple: (is_valid, error_message)
    """
    if start_time >= end_time:
        return False, "End time must be after start time."
    return True, None


def auto_complete_past_events():
    """Automatically mark events as completed when the event date has passed"""
    today = datetime.now().date()
    
    # Find all scheduled events with date < today
    past_events = GrassrootsEvent.query.filter(
        GrassrootsEvent.event_date < today,
        GrassrootsEvent.status == 'scheduled',
        GrassrootsEvent.is_active == True
    ).all()
    
    completed_count = 0
    for event in past_events:
        event.status = 'completed'
        event.updated_at = datetime.utcnow()
        completed_count += 1
    
    if completed_count > 0:
        db.session.commit()
        print(f"Auto-completed {completed_count} past events")
    
    return completed_count


# ============================================================
# ROUTES
# ============================================================

@app.route('/')
def index():
    return redirect(url_for('login'))


@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        
        user = User.query.filter(
            (User.username == username) | (User.email == username)
        ).first()
        
        if user and user.check_password(password) and user.is_active:
            login_user(user)
            user.last_login_at = datetime.utcnow()
            db.session.commit()
            flash(f'Welcome back, {user.username}!', 'success')
            return redirect(url_for('dashboard'))
        else:
            flash('Invalid username/email or password.', 'danger')
    
    # Pass None for user on login page
    return render_template('login.html', user=None)


@app.route('/reset-password', methods=['GET', 'POST'])
@login_required
def reset_password():
    # Only allow admins to reset passwords
    if not current_user.is_admin:
        flash('Access denied.', 'danger')
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        username = request.form.get('username')
        new_password = request.form.get('new_password')
        confirm_password = request.form.get('confirm_password')
        
        if new_password != confirm_password:
            flash('Passwords do not match.', 'danger')
            return redirect(url_for('reset_password'))
        
        user = User.query.filter_by(username=username).first()
        if user:
            user.set_password(new_password)
            db.session.commit()
            flash(f'Password changed for user: {username}', 'success')
        else:
            flash(f'User "{username}" not found.', 'danger')
        
        return redirect(url_for('reset_password'))
    
    users = User.query.all()
    return render_template('reset_password.html', users=users)


@app.route('/admin/reset-password', methods=['GET', 'POST'])
@login_required
def admin_reset_password():
    # Only allow admin and super_admin to access
    if not (current_user.is_admin or current_user.is_super_admin):
        flash('Access denied. Admin privileges required.', 'danger')
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        user_id = request.form.get('user_id')
        new_password = request.form.get('new_password')
        confirm_password = request.form.get('confirm_password')
        
        # Validate password match
        if new_password != confirm_password:
            flash('Passwords do not match.', 'danger')
            return redirect(url_for('admin_reset_password'))
        
        # Validate password length
        if len(new_password) < 6:
            flash('Password must be at least 6 characters long.', 'danger')
            return redirect(url_for('admin_reset_password'))
        
        # Find the user
        user = User.query.get(user_id)
        if not user:
            flash('User not found.', 'danger')
            return redirect(url_for('admin_reset_password'))
        
        # Prevent admin from changing their own password via this form (optional security)
        if user.id == current_user.id:
            flash('Use Profile page to change your own password.', 'warning')
            return redirect(url_for('admin_reset_password'))
        
        # Change the password
        user.set_password(new_password)
        db.session.commit()
        
        flash(f'Password changed successfully for user: {user.username}', 'success')
        return redirect(url_for('admin_reset_password'))
    
    # GET request - show the form
    # Get all users except the current admin (optional)
    users = User.query.filter(User.id != current_user.id).order_by(User.username).all()
    
    return render_template('admin_reset_password.html', users=users)


@app.route('/profile/change-password', methods=['POST'])
@login_required
def change_password():
    current_password = request.form.get('current_password')
    new_password = request.form.get('new_password')
    confirm_password = request.form.get('confirm_password')
    
    # Verify current password
    if not current_user.check_password(current_password):
        flash('Current password is incorrect.', 'danger')
        return redirect(url_for('profile'))
    
    # Check new password length
    if len(new_password) < 6:
        flash('New password must be at least 6 characters.', 'danger')
        return redirect(url_for('profile'))
    
    # Check password match
    if new_password != confirm_password:
        flash('New passwords do not match.', 'danger')
        return redirect(url_for('profile'))
    
    # Update password
    current_user.set_password(new_password)
    db.session.commit()
    
    flash('Your password has been changed successfully!', 'success')
    return redirect(url_for('profile'))


@app.route('/logout')
@login_required
def logout():
    logout_user()
    flash('You have been logged out.', 'info')
    return redirect(url_for('login'))


@app.route('/dashboard')
@login_required
def dashboard():
    # Auto-complete past events
    auto_complete_past_events()
    
    # Get event statistics
    company_id = current_user.company_id
    
    # Get filter parameters
    filter_type = request.args.get('filter', 'all')  # all, subdivision, myself
    selected_subdivision = request.args.get('subdivision', '')
    
    today = datetime.now().date()
    twelve_months_later = today + timedelta(days=365)
    
    # Base query for upcoming events (next 12 months)
    query = GrassrootsEvent.query.filter(
        GrassrootsEvent.company_id == company_id,
        GrassrootsEvent.is_active == True,
        GrassrootsEvent.event_date >= today,
        GrassrootsEvent.event_date <= twelve_months_later,
        GrassrootsEvent.status == 'scheduled'
    )
    
    # Apply department/constituency filter - users can only see their department's events
    if not current_user.is_admin and not current_user.is_super_admin:
        # Non-admin users can only see events in their department/constituency
        if current_user.constituency:
            query = query.filter(GrassrootsEvent.constituency == current_user.constituency)
        else:
            # No constituency assigned - show nothing
            query = query.filter(GrassrootsEvent.id == None)
    
    # Apply additional filters based on user selection
    if filter_type == 'subdivision' and selected_subdivision:
        # Filter by specific sub_division
        query = query.filter(GrassrootsEvent.sub_division == selected_subdivision)
    elif filter_type == 'myself' and current_user.is_organising_gro:
        # Show only events where current user is the organising GRO
        query = query.filter(GrassrootsEvent.organising_gro == current_user.username)
    # 'all' shows all events in the department (default)
    
    # Get all upcoming events (for the next 12 months)
    upcoming_events = query.order_by(GrassrootsEvent.event_date).all()
    
    # Group events by month for better display
    events_by_month = {}
    for event in upcoming_events:
        month_key = event.event_date.strftime('%Y-%m')
        month_name = event.event_date.strftime('%B %Y')
        if month_key not in events_by_month:
            events_by_month[month_key] = {
                'name': month_name,
                'events': []
            }
        events_by_month[month_key]['events'].append(event)
    
    # FIXED: Get unique sub_divisions for filter dropdown
    # Build a separate query for subdivisions without the complex subquery
    subdivisions_base_query = GrassrootsEvent.query.filter(
        GrassrootsEvent.company_id == company_id,
        GrassrootsEvent.is_active == True,
        GrassrootsEvent.event_date >= today,
        GrassrootsEvent.status == 'scheduled'
    )
    
    # Apply department filter for subdivisions
    if not current_user.is_admin and not current_user.is_super_admin and current_user.constituency:
        subdivisions_base_query = subdivisions_base_query.filter(
            GrassrootsEvent.constituency == current_user.constituency
        )
    
    # Get distinct sub_division values - FIXED: Use .distinct() on a single column
    unique_subdivisions = []
    try:
        # Query just the sub_division column distinctly
        subdivision_results = subdivisions_base_query.with_entities(
            GrassrootsEvent.sub_division
        ).filter(
            GrassrootsEvent.sub_division.isnot(None),
            GrassrootsEvent.sub_division != ''
        ).distinct().order_by(GrassrootsEvent.sub_division).all()
        
        # Extract values from tuples
        unique_subdivisions = [s[0] for s in subdivision_results if s[0]]
    except Exception as e:
        # Fallback - get from all events if the filtered query fails
        print(f"Error getting subdivisions: {e}")
        unique_subdivisions = []
    
    # Statistics
    total_events_query = GrassrootsEvent.query.filter(
        GrassrootsEvent.company_id == company_id,
        GrassrootsEvent.is_active == True
    )
    
    upcoming_count = query.count()
    
    # Apply department filter for other stats
    if not current_user.is_admin and not current_user.is_super_admin and current_user.constituency:
        total_events_query = total_events_query.filter(
            GrassrootsEvent.constituency == current_user.constituency
        )
    
    total_events_count = total_events_query.count()
    completed_count = total_events_query.filter(
        GrassrootsEvent.event_date < today
    ).count()
    
    # My events count (for organising GRO)
    my_events_count = 0
    if current_user.is_organising_gro:
        my_events_query = GrassrootsEvent.query.filter(
            GrassrootsEvent.company_id == company_id,
            GrassrootsEvent.organising_gro == current_user.username,
            GrassrootsEvent.is_active == True
        )
        if not current_user.is_admin and not current_user.is_super_admin and current_user.constituency:
            my_events_query = my_events_query.filter(
                GrassrootsEvent.constituency == current_user.constituency
            )
        my_events_count = my_events_query.count()
    
    stats = {
        'total_events': total_events_count,
        'upcoming_events': upcoming_count,
        'completed_events': completed_count,
        'my_events': my_events_count
    }
    
    return render_template(
        'dashboard.html',
        stats=stats,
        events_by_month=events_by_month,
        upcoming_events=upcoming_events,
        filter_type=filter_type,
        selected_subdivision=selected_subdivision,
        unique_subdivisions=unique_subdivisions,
        user=current_user,
        today=today,
        twelve_months_later=twelve_months_later
    )


@app.route('/events')
@login_required
def events():
    company_id = current_user.company_id
    
    # Get filter parameters
    status = request.args.get('status', 'upcoming')
    my_only = request.args.get('my_only', 'false').lower() == 'true'
    search = request.args.get('search', '')
    
    today = datetime.now().date()
    twelve_months_later = today + timedelta(days=365)
    
    # Base query with permission filtering
    query = GrassrootsEvent.query.filter_by(company_id=company_id, is_active=True)
    
    # Apply constituency filter for non-admin users
    if not current_user.is_admin and not current_user.is_super_admin:
        if my_only or current_user.is_organising_gro:
            # Show only user's own events
            query = query.filter(GrassrootsEvent.organising_gro == current_user.username)
        elif current_user.constituency:
            # Show events in user's constituency
            query = query.filter(GrassrootsEvent.constituency == current_user.constituency)
        else:
            # No constituency assigned - show nothing
            query = query.filter(GrassrootsEvent.id == None)
    
    # Apply status filters
    if status == 'upcoming':
        query = query.filter(
            GrassrootsEvent.event_date >= today,
            GrassrootsEvent.event_date <= twelve_months_later,
            GrassrootsEvent.status == 'scheduled'
        )
        # Upcoming events: chronological order (soonest first)
        query = query.order_by(GrassrootsEvent.event_date.asc(), GrassrootsEvent.start_time.asc())
    elif status == 'past':
        query = query.filter(GrassrootsEvent.event_date < today)
        # Past events: newest first
        query = query.order_by(GrassrootsEvent.event_date.desc())
    elif status == 'cancelled':
        query = query.filter(GrassrootsEvent.status == 'cancelled')
        query = query.order_by(GrassrootsEvent.event_date.desc())
    else:
        # All events
        query = query.order_by(GrassrootsEvent.event_date.desc())
    
    # Apply search filter
    if search:
        query = query.filter(
            db.or_(
                GrassrootsEvent.name.ilike(f'%{search}%'),
                GrassrootsEvent.venue.ilike(f'%{search}%'),
                GrassrootsEvent.organising_gro.ilike(f'%{search}%')
            )
        )
    
    events_list = query.all()
    
    return render_template(
        'events.html',
        events=events_list,
        status=status,
        my_only=my_only,
        search=search,
        user=current_user,
        today=today  # Add this line
    )


@app.route('/events/create', methods=['GET', 'POST'])
@login_required
def create_event():
    # Only admins and organising GRO can create events
    if not (current_user.is_admin or current_user.is_super_admin or current_user.is_organising_gro):
        flash('You do not have permission to create events.', 'danger')
        return redirect(url_for('events'))
    
    if request.method == 'POST':
        try:
            # Parse date and time
            event_date = datetime.strptime(request.form.get('event_date'), '%Y-%m-%d').date()
            start_time = datetime.strptime(request.form.get('start_time'), '%H:%M').time()
            end_time = datetime.strptime(request.form.get('end_time'), '%H:%M').time()
            venue = request.form.get('venue')
            
            # Validate time order
            is_valid, error_msg = validate_event_times(event_date, start_time, end_time)
            if not is_valid:
                flash(error_msg, 'danger')
                return redirect(url_for('create_event'))
            
            # Check for venue conflicts
            has_conflict, conflicting_event = check_venue_conflict(
                venue, event_date, start_time, end_time
            )
            
            if has_conflict:
                flash(
                    f'⚠️ Venue conflict! "{venue}" is already booked on {event_date} '
                    f'from {conflicting_event.start_time.strftime("%H:%M")} to '
                    f'{conflicting_event.end_time.strftime("%H:%M")} for event: '
                    f'"{conflicting_event.name}". Please choose a different time or venue.',
                    'danger'
                )
                return redirect(url_for('create_event'))
            
            # Generate unique event UID
            import uuid
            event_uid = str(uuid.uuid4())
            
            # For organising GRO, force their constituency
            constituency = request.form.get('constituency')
            if current_user.is_organising_gro and current_user.constituency:
                constituency = current_user.constituency
            
            # Parse GRA times (optional)
            gra_start_time = None
            gra_end_time = None
            if request.form.get('gra_start_time'):
                gra_start_time = datetime.strptime(request.form.get('gra_start_time'), '%H:%M').time()
            if request.form.get('gra_end_time'):
                gra_end_time = datetime.strptime(request.form.get('gra_end_time'), '%H:%M').time()
            
            event = GrassrootsEvent(
                event_uid=event_uid,
                company_id=current_user.company_id,
                name=request.form.get('name'),
                description=request.form.get('description', ''),
                event_date=event_date,
                start_time=start_time,
                end_time=end_time,
                venue=venue,
                constituency=constituency,
                sub_division=request.form.get('sub_division', ''),
                organising_gro=current_user.username,
                organising_gro_contact=request.form.get('organising_gro_contact', ''),
                organising_gro_email=current_user.email,
                expected_participants=int(request.form.get('expected_participants', 0)),
                gra_attending=request.form.get('gra_attending') == 'on',
                gra_attending_confirmed=request.form.get('gra_attending_confirmed') == 'on',
                gra_name=request.form.get('gra_name', ''),
                gra_start_time=gra_start_time,
                gra_end_time=gra_end_time,
                status='scheduled',
                created_by_id=current_user.id,
                created_at=datetime.utcnow()
            )
            
            db.session.add(event)
            db.session.commit()

            if event:
                # Update venue usage count
                venue_record = Venue.query.filter_by(
                    company_id=current_user.company_id,
                    name=event.venue,
                    created_by_id=current_user.id
                ).first()
                
                if venue_record:
                    venue_record.usage_count += 1
                    venue_record.last_used_at = datetime.utcnow()
                    db.session.commit()


            flash('Event created successfully!', 'success')
            return redirect(url_for('events'))
            
        except Exception as e:
            db.session.rollback()
            flash(f'Error creating event: {str(e)}', 'danger')
    
        
    # GET request - show form with suggested venues (top 8)
    suggested_venues = []
    if current_user.is_organising_gro:
        suggested_venues = get_suggested_venues_for_gro(current_user, limit=MAX_SUGGESTED_VENUES)
    
    return render_template('event_form.html', event=None, user=current_user, suggested_venues=suggested_venues)


@app.route('/events/<int:event_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_event(event_id):
    event = GrassrootsEvent.query.get_or_404(event_id)
    
    # Check permission
    if not event.can_edit(current_user):
        flash('You do not have permission to edit this event.', 'danger')
        return redirect(url_for('events'))
    
    if request.method == 'POST':
        try:
            # Parse date and time
            event_date = datetime.strptime(request.form.get('event_date'), '%Y-%m-%d').date()
            start_time = datetime.strptime(request.form.get('start_time'), '%H:%M').time()
            end_time = datetime.strptime(request.form.get('end_time'), '%H:%M').time()
            venue = request.form.get('venue')
            
            # Validate time order
            is_valid, error_msg = validate_event_times(event_date, start_time, end_time)
            if not is_valid:
                flash(error_msg, 'danger')
                return redirect(url_for('edit_event', event_id=event_id))
            
            # Check for venue conflicts (excluding current event)
            has_conflict, conflicting_event = check_venue_conflict(
                venue, event_date, start_time, end_time, exclude_event_id=event_id
            )
            
            if has_conflict:
                flash(
                    f'⚠️ Venue conflict! "{venue}" is already booked on {event_date} '
                    f'from {conflicting_event.start_time.strftime("%H:%M")} to '
                    f'{conflicting_event.end_time.strftime("%H:%M")} for event: '
                    f'"{conflicting_event.name}". Please choose a different time or venue.',
                    'danger'
                )
                return redirect(url_for('edit_event', event_id=event_id))
            
            # Parse GRA times (optional)
            gra_start_time = None
            gra_end_time = None
            if request.form.get('gra_start_time'):
                gra_start_time = datetime.strptime(request.form.get('gra_start_time'), '%H:%M').time()
            if request.form.get('gra_end_time'):
                gra_end_time = datetime.strptime(request.form.get('gra_end_time'), '%H:%M').time()
            
            # Update event fields
            event.name = request.form.get('name')
            event.description = request.form.get('description', '')
            event.event_date = event_date
            event.start_time = start_time
            event.end_time = end_time
            event.venue = venue
            event.constituency = request.form.get('constituency')
            event.sub_division = request.form.get('sub_division')
            event.organising_gro_contact = request.form.get('organising_gro_contact', '')
            event.expected_participants = int(request.form.get('expected_participants', 0))
            event.gra_attending = request.form.get('gra_attending') == 'on'
            event.gra_attending_confirmed = request.form.get('gra_attending_confirmed') == 'on'
            event.gra_name = request.form.get('gra_name', '')
            event.gra_start_time = gra_start_time
            event.gra_end_time = gra_end_time
            event.status = request.form.get('status', 'scheduled')
            event.updated_by_id = current_user.id
            event.updated_at = datetime.utcnow()
            
            db.session.commit()
            flash('Event updated successfully!', 'success')
            return redirect(url_for('events'))
            
        except Exception as e:
            db.session.rollback()
            flash(f'Error updating event: {str(e)}', 'danger')
    
        
    # GET request - show form with suggested venues
    suggested_venues = []
    if current_user.is_organising_gro:
        suggested_venues = get_suggested_venues_for_gro(current_user, limit=MAX_SUGGESTED_VENUES)
    
    return render_template('event_form.html', event=event, user=current_user, suggested_venues=suggested_venues)


@app.route('/events/<int:event_id>/delete', methods=['POST'])
@login_required
@manager_required
def delete_event(event_id):
    event = GrassrootsEvent.query.get_or_404(event_id)
    
    if not event.can_edit(current_user):
        flash('You do not have permission to delete this event.', 'danger')
        return redirect(url_for('events'))
    
    try:
        event.is_active = False
        event.status = 'cancelled'
        event.updated_by_id = current_user.id
        db.session.commit()
        flash('Event cancelled successfully.', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'Error cancelling event: {str(e)}', 'danger')
    
    return redirect(url_for('events'))


@app.route('/api/events')
@login_required
def api_events():
    """JSON API for calendar view"""
    company_id = current_user.company_id
    start_date = request.args.get('start')
    end_date = request.args.get('end')
    
    try:
        start = datetime.strptime(start_date, '%Y-%m-%d').date() if start_date else None
        end = datetime.strptime(end_date, '%Y-%m-%d').date() if end_date else None
    except:
        start = None
        end = None
    
    query = GrassrootsEvent.query.filter_by(company_id=company_id, is_active=True)
    
    if start:
        query = query.filter(GrassrootsEvent.event_date >= start)
    if end:
        query = query.filter(GrassrootsEvent.event_date <= end)
    
    events = query.all()
    return jsonify([e.to_dict() for e in events])


@app.route('/api/check-venue-conflict', methods=['POST'])
@login_required
def api_check_venue_conflict():
    """AJAX endpoint to check venue availability in real-time"""
    try:
        data = request.get_json()
        venue = data.get('venue')
        event_date = datetime.strptime(data.get('event_date'), '%Y-%m-%d').date()
        start_time = datetime.strptime(data.get('start_time'), '%H:%M').time()
        end_time = datetime.strptime(data.get('end_time'), '%H:%M').time()
        exclude_id = data.get('exclude_id', None)
        
        # Validate time order
        if start_time >= end_time:
            return jsonify({
                'has_conflict': True,
                'message': 'End time must be after start time.',
                'conflict_type': 'invalid_time'
            })
        
        has_conflict, conflicting_event = check_venue_conflict(
            venue, event_date, start_time, end_time, exclude_id
        )
        
        if has_conflict:
            return jsonify({
                'has_conflict': True,
                'message': f'Venue is already booked from {conflicting_event.start_time.strftime("%H:%M")} to {conflicting_event.end_time.strftime("%H:%M")} for "{conflicting_event.name}"',
                'conflict_event': {
                    'name': conflicting_event.name,
                    'start_time': conflicting_event.start_time.strftime('%H:%M'),
                    'end_time': conflicting_event.end_time.strftime('%H:%M')
                }
            })
        else:
            return jsonify({'has_conflict': False, 'message': 'Venue available'})
            
    except Exception as e:
        return jsonify({'has_conflict': True, 'message': f'Error checking availability: {str(e)}'})


# ============================================================
# VENUE MANAGEMENT ROUTES
# ============================================================

@app.route('/venues')
@login_required
def venues():
    """Manage venues - only for organising_gro and admin"""
    if not (current_user.is_admin or current_user.is_organising_gro):
        flash('You do not have permission to manage venues.', 'danger')
        return redirect(url_for('dashboard'))
    
    # Get venues created by this user
    if current_user.is_admin:
        venues_list = Venue.query.filter_by(
            company_id=current_user.company_id, 
            is_active=True
        ).order_by(Venue.usage_count.desc()).all()
    else:
        venues_list = Venue.query.filter_by(
            company_id=current_user.company_id,
            created_by_id=current_user.id,
            is_active=True
        ).order_by(Venue.usage_count.desc()).all()
    
    # Get shared venues (from other GROs in same constituency)
    shared_venues = []
    if not current_user.is_admin and current_user.constituency:
        shared_venues = db.session.query(
            Venue.name,
            db.func.sum(Venue.usage_count).label('total_usage'),
            Venue.constituency,
            Venue.capacity
        ).filter(
            Venue.company_id == current_user.company_id,
            Venue.is_active == True,
            Venue.constituency == current_user.constituency,
            Venue.created_by_id != current_user.id
        ).group_by(Venue.name, Venue.constituency, Venue.capacity).order_by(
            db.func.sum(Venue.usage_count).desc()
        ).limit(20).all()
        
        # Convert to list of dicts for easier template access
        shared_venues = [
            {
                'name': sv[0],
                'total_usage': sv[1],
                'constituency': sv[2],
                'capacity': sv[3]
            }
            for sv in shared_venues
        ]
    
    # Get frequently used venues from events
    frequent_venues = db.session.query(
        GrassrootsEvent.venue, 
        db.func.count(GrassrootsEvent.id).label('usage_count')
    ).filter(
        GrassrootsEvent.company_id == current_user.company_id,
        GrassrootsEvent.organising_gro == current_user.username,
        GrassrootsEvent.is_active == True
    ).group_by(GrassrootsEvent.venue).order_by(db.func.count(GrassrootsEvent.id).desc()).limit(10).all()
    
    return render_template(
        'venues.html', 
        venues=venues_list, 
        shared_venues=shared_venues,
        frequent_venues=frequent_venues, 
        user=current_user
    )


@app.route('/venues/create', methods=['GET', 'POST'])
@login_required
def create_venue():
    """Create a new permanent venue or adopt an existing one"""
    if not (current_user.is_admin or current_user.is_organising_gro):
        flash('You do not have permission to create venues.', 'danger')
        return redirect(url_for('dashboard'))
    
    if request.method == 'POST':
        try:
            venue_name = request.form.get('name')
            constituency = request.form.get('constituency', current_user.constituency or '')
            
            # Check if venue already exists in the same company (created by anyone)
            existing_venue = Venue.query.filter_by(
                company_id=current_user.company_id,
                name=venue_name,
                is_active=True
            ).first()
            
            if existing_venue:
                # Venue exists - check if current user already has it
                user_venue = Venue.query.filter_by(
                    company_id=current_user.company_id,
                    created_by_id=current_user.id,
                    name=venue_name,
                    is_active=True
                ).first()
                
                if user_venue:
                    flash('You already have this venue in your list.', 'warning')
                    return redirect(url_for('venues'))
                
                # Create a copy for the current user (allow sharing)
                venue = Venue(
                    company_id=current_user.company_id,
                    created_by_id=current_user.id,
                    name=existing_venue.name,
                    address=request.form.get('address', existing_venue.address or ''),
                    constituency=constituency,
                    sub_division=request.form.get('sub_division', current_user.sub_division or ''),
                    capacity=int(request.form.get('capacity', existing_venue.capacity or 0)),
                    is_permanent=True,
                    is_default=request.form.get('is_default') == 'on',
                    created_at=datetime.utcnow(),
                    usage_count=existing_venue.usage_count  # Inherit usage count from original
                )
                
                # If this is set as default, unset other defaults for this user
                if venue.is_default:
                    Venue.query.filter_by(
                        company_id=current_user.company_id,
                        created_by_id=current_user.id,
                        is_default=True
                    ).update({'is_default': False})
                
                db.session.add(venue)
                db.session.commit()
                flash(f'Venue "{venue.name}" added to your list (shared from another GRO)!', 'success')
                return redirect(url_for('venues'))
            
            # Create brand new venue
            venue = Venue(
                company_id=current_user.company_id,
                created_by_id=current_user.id,
                name=venue_name,
                address=request.form.get('address', ''),
                constituency=constituency,
                sub_division=request.form.get('sub_division', current_user.sub_division or ''),
                capacity=int(request.form.get('capacity', 0)),
                is_permanent=True,
                is_default=request.form.get('is_default') == 'on',
                created_at=datetime.utcnow()
            )
            
            # If this is set as default, unset other defaults
            if venue.is_default:
                Venue.query.filter_by(
                    company_id=current_user.company_id,
                    created_by_id=current_user.id,
                    is_default=True
                ).update({'is_default': False})
            
            db.session.add(venue)
            db.session.commit()
            flash(f'Venue "{venue.name}" created successfully!', 'success')
            return redirect(url_for('venues'))
            
        except Exception as e:
            db.session.rollback()
            flash(f'Error creating venue: {str(e)}', 'danger')
    
    return render_template('venue_form.html', venue=None, user=current_user)


@app.route('/venues/<int:venue_id>/edit', methods=['GET', 'POST'])
@login_required
def edit_venue(venue_id):
    """Edit a venue"""
    venue = Venue.query.get_or_404(venue_id)
    
    # Check permission
    if not (current_user.is_admin or venue.created_by_id == current_user.id):
        flash('You do not have permission to edit this venue.', 'danger')
        return redirect(url_for('venues'))
    
    if request.method == 'POST':
        try:
            venue.name = request.form.get('name')
            venue.address = request.form.get('address', '')
            venue.constituency = request.form.get('constituency', current_user.constituency or '')
            venue.sub_division = request.form.get('sub_division', current_user.sub_division or '')
            venue.capacity = int(request.form.get('capacity', 0))
            
            was_default = venue.is_default
            venue.is_default = request.form.get('is_default') == 'on'
            
            # If this is set as default, unset other defaults
            if venue.is_default and not was_default:
                Venue.query.filter_by(
                    company_id=current_user.company_id,
                    created_by_id=current_user.id,
                    is_default=True
                ).filter(Venue.id != venue_id).update({'is_default': False})
            
            venue.updated_at = datetime.utcnow()
            db.session.commit()
            flash(f'Venue "{venue.name}" updated successfully!', 'success')
            return redirect(url_for('venues'))
            
        except Exception as e:
            db.session.rollback()
            flash(f'Error updating venue: {str(e)}', 'danger')
    
    return render_template('venue_form.html', venue=venue, user=current_user)


@app.route('/venues/<int:venue_id>/delete', methods=['POST'])
@login_required
def delete_venue(venue_id):
    """Soft delete a venue"""
    venue = Venue.query.get_or_404(venue_id)
    
    if not (current_user.is_admin or venue.created_by_id == current_user.id):
        flash('You do not have permission to delete this venue.', 'danger')
        return redirect(url_for('venues'))
    
    try:
        venue.is_active = False
        db.session.commit()
        flash(f'Venue "{venue.name}" deleted successfully.', 'success')
    except Exception as e:
        db.session.rollback()
        flash(f'Error deleting venue: {str(e)}', 'danger')
    
    return redirect(url_for('venues'))


@app.route('/venues/add-shared/<venue_name>', methods=['POST'])
@login_required
def add_shared_venue(venue_name):
    """Add a shared venue (used by other GROs) to current user's venues"""
    if not (current_user.is_admin or current_user.is_organising_gro):
        flash('You do not have permission to add venues.', 'danger')
        return redirect(url_for('venues'))
    
    # Find the original venue
    original_venue = Venue.query.filter_by(
        company_id=current_user.company_id,
        name=venue_name,
        is_active=True
    ).first()
    
    if not original_venue:
        flash('Venue not found.', 'danger')
        return redirect(url_for('venues'))
    
    # Check if user already has this venue
    existing = Venue.query.filter_by(
        company_id=current_user.company_id,
        created_by_id=current_user.id,
        name=venue_name,
        is_active=True
    ).first()
    
    if existing:
        flash('You already have this venue in your list.', 'warning')
        return redirect(url_for('venues'))
    
    # Create a copy for the current user
    new_venue = Venue(
        company_id=current_user.company_id,
        created_by_id=current_user.id,
        name=original_venue.name,
        address=original_venue.address,
        constituency=current_user.constituency or original_venue.constituency,
        sub_division=current_user.sub_division or original_venue.sub_division,
        capacity=original_venue.capacity,
        is_permanent=True,
        is_default=False,  # Don't set as default by default
        usage_count=original_venue.usage_count,  # Inherit usage count
        last_used_at=original_venue.last_used_at,
        created_at=datetime.utcnow()
    )
    
    db.session.add(new_venue)
    db.session.commit()
    
    flash(f'Venue "{venue_name}" added to your venues!', 'success')
    return redirect(url_for('venues'))


@app.route('/api/venues/frequent')
@login_required
def api_frequent_venues():
    """Get frequently used venues for the current user (for AJAX)"""
    venues = db.session.query(
        GrassrootsEvent.venue, 
        db.func.count(GrassrootsEvent.id).label('usage_count')
    ).filter(
        GrassrootsEvent.company_id == current_user.company_id,
        GrassrootsEvent.organising_gro == current_user.username,
        GrassrootsEvent.is_active == True
    ).group_by(GrassrootsEvent.venue).order_by(db.func.count(GrassrootsEvent.id).desc()).limit(10).all()
    
    return jsonify([{'name': v[0], 'count': v[1]} for v in venues])


@app.route('/api/save-venue-to-permanent', methods=['POST'])
@login_required
def api_save_venue_to_permanent():
    """Save an ad-hoc venue to user's permanent venues"""
    if not current_user.is_organising_gro:
        return jsonify({'success': False, 'message': 'Permission denied'}), 403
    
    data = request.get_json()
    venue_name = data.get('venue_name')
    
    if not venue_name:
        return jsonify({'success': False, 'message': 'Venue name required'}), 400
    
    # Check if already exists
    existing = Venue.query.filter_by(
        company_id=current_user.company_id,
        created_by_id=current_user.id,
        name=venue_name,
        is_active=True
    ).first()
    
    if existing:
        return jsonify({'success': False, 'message': 'Venue already in your list'}), 400
    
    # Get usage count from events
    usage_count = GrassrootsEvent.query.filter(
        GrassrootsEvent.company_id == current_user.company_id,
        GrassrootsEvent.organising_gro == current_user.username,
        GrassrootsEvent.venue == venue_name,
        GrassrootsEvent.is_active == True
    ).count()
    
    # Create permanent venue
    new_venue = Venue(
        company_id=current_user.company_id,
        created_by_id=current_user.id,
        name=venue_name,
        constituency=current_user.constituency,
        sub_division=current_user.sub_division,
        is_permanent=True,
        is_default=False,
        usage_count=usage_count,
        created_at=datetime.utcnow()
    )
    
    db.session.add(new_venue)
    db.session.commit()
    
    return jsonify({'success': True, 'message': f'Venue "{venue_name}" saved to your permanent venues!'})


@app.route('/profile')
@login_required
def profile():
    return render_template('profile.html', user=current_user)


# ============================================================
# INITIALIZATION
# ============================================================

def init_db():
    """Create tables and default users with constituencies"""
    db.create_all()
    
    # Create company
    company = Company.query.first()
    if not company:
        # Create default company only if no companies exist
        company = Company(
            name='PEOPLES ASSOCIATION', 
            domain='pa.gov.sg',
            is_active=True
        )
        db.session.add(company)
        db.session.commit()
        print(" Company created")
    else:
        print(f" Company already exists: {company.name}")
    
    # Create constituency users
    constituencies = ['Tanjong Pagar-Tiong Bahru', 'Ang Mo Kio', 'Bishan-Toa Payoh']
    
    # Admin user (full access)
    try:
        admin = User.query.filter_by(username='paadmin').first()
        if not admin:
            admin = User(
                username='paadmin',
                email='paadmin@synzhi.com',
                role='admin',
                company_id=company.id,
                department='Administration',
                sub_division='HQ',
                is_active=True
            )
            admin.set_password('admin123')
            db.session.add(admin)
            db.session.flush()  # ← Force insert to get ID
            db.session.commit()
            print(" Default admin user created: paadmin")
        else:
            print(" Admin user already exists")
    except Exception as e:
        print(f" Error creating admin user: {e}")
        db.session.rollback()
    
    print(" Database initialized")


if __name__ == '__main__':
    with app.app_context():
        init_db()
    
    port = int(os.environ.get('PORT', 5051))
    host = os.environ.get('HOST', '0.0.0.0')
    debug = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
    
    app.run(host=host, port=port, debug=debug)
