from flask import Flask, app, render_template, redirect, url_for,current_app, send_from_directory, abort
from werkzeug.utils import secure_filename, safe_join
import os
from config import Config
from extensions import db, bcrypt, login_manager
from routes.master import master_bp
from datetime import datetime, date
from flask_login import current_user, login_required
from models import Notification,DemoCompany, DemoAccount, Employee
from sqlalchemy import or_, desc
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_migrate import Migrate
import click
from flask.cli import with_appcontext


migrate = Migrate()

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)

    app.config.setdefault('ALLOWED_DOC_EXTENSIONS', {'pdf','png','jpg','jpeg','webp','doc','docx'})
    app.config.setdefault('MAX_CONTENT_LENGTH', 20 * 1024 * 1024)  # 20 MB
    app.config.setdefault('DOCUMENTS_UPLOAD_FOLDER', os.path.join(app.root_path, 'uploads', 'employee_docs'))
    os.makedirs(app.config['DOCUMENTS_UPLOAD_FOLDER'], exist_ok=True)
    migrate.init_app(app, db)

    app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1)
    app.config.setdefault("PREFERRED_URL_SCHEME", "https")
    @app.route('/')
    def index():
        return redirect(url_for('employee.employee_login'))



    # Initialize extensions
    db.init_app(app)
    bcrypt.init_app(app)
    login_manager.init_app(app)
    login_manager.login_view = 'auth.login'
   


    app.config.setdefault('UPLOAD_FOLDER', os.path.join(app.root_path, 'uploads'))

    @app.cli.command('cleanup-expired-demos')
    @with_appcontext
    def cleanup_expired_demos():
        """Delete expired demo companies, their demo accounts and demo employees."""
        today = date.today()
        expired_companies = DemoCompany.query.filter(
            DemoCompany.expires_on.isnot(None),
            DemoCompany.expires_on < today
        ).all()

        deleted_emps = 0
        deleted_companies = len(expired_companies)

        for company in expired_companies:
            for acc in company.demo_accounts:
                if acc.employee_id:
                    emp = Employee.query.get(acc.employee_id)
                    if emp:
                        try:
                            db.session.delete(emp)
                            deleted_emps += 1
                        except Exception:
                            # If there are FK constraints, just deactivate instead
                            emp.status = 'inactive'
                db.session.delete(acc)

            db.session.delete(company)

        db.session.commit()
        click.echo(f"Deleted {deleted_companies} demo companies and {deleted_emps} demo employees.")

    @app.route('/uploads/<path:subpath>')
    @login_required
    def serve_upload(subpath: str):
        # Quick access control: owner or HR/Admin
        parts = subpath.split('/', 1)
        if parts and parts[0].isdigit():
            owner_id = int(parts[0])
            role_name = (getattr(getattr(current_user, 'role', None), 'name', '') or '').lower()
            is_hr_admin = role_name in ('hr', 'admin', 'hr admin')
            if current_user.id != owner_id and not is_hr_admin:
                abort(403)

        root = current_app.config['UPLOAD_FOLDER']
        full = safe_join(root, subpath)
        if not full or not os.path.isfile(full):
            abort(404)

        # Stream with ETag/range support; no caching by default
        return send_from_directory(root, subpath, conditional=True, max_age=0)
    
    from datetime import datetime
    from flask import current_app
    from models import BrandingSetting  # we'll add this model in step 3
    from sqlalchemy.exc import OperationalError
    @app.context_processor
    def inject_theme():
        # Safe defaults (your current palette)
        defaults = {
            "sidebar_bg": current_app.config.get("BRAND_SIDEBAR_COLOR", "#0054A6"),
            "topbar_bg":  current_app.config.get("BRAND_TOPBAR_COLOR",  "#0054A6"),
            "accent":     current_app.config.get("BRAND_ACCENT_COLOR",   "#F47C20"),
            "logo_url":   current_app.config.get("BRAND_LOGO_URL", url_for('static', filename='images/logo.png')),
            "version":    int(datetime.utcnow().timestamp()),
        }
        try:
            bs = BrandingSetting.query.get(1)
            if not bs:
                return dict(theme=defaults)
            return dict(theme={
                "sidebar_bg": bs.sidebar_bg or defaults["sidebar_bg"],
                "topbar_bg":  bs.topbar_bg  or defaults["topbar_bg"],
                "accent":     bs.accent     or defaults["accent"],
                "logo_url":   bs.logo_url   or defaults["logo_url"],
                "version":    int(bs.updated_at.timestamp()) if bs.updated_at else defaults["version"],
            })
        except OperationalError:
            # During first migrate/init
            return dict(theme=defaults)

    @app.context_processor
    def inject_sidebar_links():
        from models import Menu, RoleAccess, UserAccess, SubMenu
        from flask_login import current_user

        if not current_user.is_authenticated:
            return {}

        role_name = (getattr(getattr(current_user, 'role', None), 'name', '') or '').strip().lower()

        # Grab role-based & user-specific submenu IDs
        role_ids = [ra.submenu_id for ra in RoleAccess.query.filter_by(role=role_name).all()]
        user_ids = [ua.submenu_id for ua in UserAccess.query.filter_by(user_id=current_user.id).all()]

        # Union: role access + additional user-specific
        allowed_ids = set(role_ids) | set(user_ids)

        # Safety net: if admin somehow has no rows, show everything
        if role_name == 'admin' and not allowed_ids:
            allowed_ids = {sid for (sid,) in db.session.query(SubMenu.id).all()}

        # Build menu tree, ordered
        menus = (Menu.query
                .options(db.joinedload(Menu.submenus))
                .order_by(Menu.order.asc(), Menu.name.asc())
                .all())

        allowed_menus = []
        for m in menus:
            subs = [s for s in sorted(m.submenus, key=lambda x: (x.order or 0, (x.name or '').lower()))
                    if s.id in allowed_ids]
            if subs:
                allowed_menus.append({
                    'name': m.name,
                    'icon': m.icon,
                    'submenus': subs
                })

        return dict(sidebar_menus=allowed_menus)
    
    @app.context_processor
    def inject_notifications():
            try:
                if not current_user.is_authenticated:
                    return dict(notifications_unread_count=0, notifications=[])

                # current_user is Employee -> use its primary key
                employee_id = getattr(current_user, "id", None)
                if not employee_id:
                    return dict(notifications_unread_count=0, notifications=[])

                base_q = Notification.query.filter(
                    or_(Notification.employee_id == employee_id,
                        Notification.employee_id.is_(None))  # <-- broadcasts
                )

                unread_count = base_q.filter(Notification.is_read.is_(False)).count()
                recent = base_q.order_by(desc(Notification.created_at)).limit(10).all()

                return dict(
                    notifications_unread_count=unread_count,
                    notifications=recent
                )
            except Exception:
                db.session.rollback()
                return dict(notifications_unread_count=0, notifications=[])

    @app.errorhandler(403)
    def forbidden(e):
        return render_template('403.html'), 403

    # Register blueprints inside app context to avoid circular import
    with app.app_context():
        from routes.auth import auth_bp
        from routes.employee import employee_bp
        from routes.dashboard import dashboard_bp
        from routes.broadcast import broadcast_bp
        from routes.leave import leave_bp
        from cli.leave_cli import auto_credit_leaves
        from routes.attendance import attendance_bp
        from routes.payroll import payroll_bp
        from cli.attendance_cli import process_attendance
        from routes.desktime_logs import desktime_logs_bp
        from admin import admin_bp
        from routes.documents import bp as documents_bp
        from routes.attendance_summary import attendance_summary_bp
        from routes.email_actions import email_actions_bp
        from routes.admin_email_logs import admin_email_logs_bp
        from routes.exit import exit_bp
        from routes.exit_lists import exit_list_bp
        from routes.notifications import notifications_bp
        from routes.feedback_admin import feedback_admin_bp
        from routes.feedback_employee import feedback_emp_bp
        from routes.feedback_reports import feedback_reports_bp
        from routes.travel import tr_bp        
        from routes.docs import docs_bp
        from commands.bootstrap import bootstrap_empty
        from routes.custom_dashboard import dashboard_bp as dashbuild  # or the file you used
        from routes.demo_public import demo_public_bp



        app.register_blueprint(demo_public_bp)
        app.register_blueprint(dashbuild)
        app.register_blueprint(docs_bp, url_prefix='/docs')
        app.register_blueprint(tr_bp)
        app.register_blueprint(feedback_reports_bp)
        app.register_blueprint(feedback_admin_bp)
        app.register_blueprint(feedback_emp_bp)
        app.register_blueprint(notifications_bp)
        app.register_blueprint(exit_list_bp)
        app.register_blueprint(exit_bp)        
        app.register_blueprint(admin_email_logs_bp)
        app.register_blueprint(email_actions_bp)
        app.register_blueprint(documents_bp, url_prefix='/documents')
        app.register_blueprint(admin_bp, url_prefix='/admin')
        app.register_blueprint(desktime_logs_bp)
        app.register_blueprint(leave_bp)
        app.register_blueprint(auth_bp, url_prefix='/auth')
        app.register_blueprint(employee_bp)
        app.register_blueprint(dashboard_bp)
        app.register_blueprint(master_bp)
        app.register_blueprint(broadcast_bp, url_prefix='/broadcast')
        app.cli.add_command(auto_credit_leaves)
        app.register_blueprint(attendance_bp)
        app.register_blueprint(payroll_bp, url_prefix='/payroll')
        app.cli.add_command(process_attendance)     
        app.register_blueprint(attendance_summary_bp)
        app.cli.add_command(bootstrap_empty)

        


    return app


# For flask shell
app = create_app()

if __name__ == "__main__":
    app.run(port=5021, debug=True)
