# File: app/__init__.py

import os
import json
import importlib
import time
import logging
from datetime import datetime

from dotenv import load_dotenv, find_dotenv
from flask import Flask, request
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate, upgrade, migrate as _migrate, stamp as _stamp
from flask_login import LoginManager
from flask_wtf.csrf import CSRFProtect

# ─── Core extensions ───────────────────────────────────────────────────────────
db            = SQLAlchemy()
migrate       = Migrate()
login_manager = LoginManager()
csrf          = CSRFProtect()


def create_app():
    # ─── Load .env ───────────────────────────────────────────────────────────────
    dotenv_path = find_dotenv()
    if dotenv_path:
        load_dotenv(dotenv_path, override=True)

    # ─── Flask setup ─────────────────────────────────────────────────────────────
    project_root   = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
    migrations_dir = os.path.join(project_root, 'migrations')

    app = Flask(
        __name__,
        static_folder=os.path.join(project_root, 'static'),
        static_url_path='/static'
    )

    # install INFO‐level logging handler
    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    app.logger.setLevel(logging.INFO)
    app.logger.addHandler(handler)
    app.logger.info("🚀 Starting create_app()")

    # main config
    app.config.from_object('app.config.Config')
    app.logger.info(f"🔧 Loaded config from {app.config.__class__.__name__}")

    # ─── Init extensions ─────────────────────────────────────────────────────────
    csrf.init_app(app)
    db.init_app(app)
    migrate.init_app(app, db, directory=migrations_dir)
    login_manager.init_app(app)
    login_manager.login_view = 'auth.login'
    app.logger.info("🔗 Initialized extensions (CSRF, SQLAlchemy, Migrate, LoginManager)")

    # ─── AUTOMATIC MIGRATIONS ────────────────────────────────────────────────────
    with app.app_context():
        app.logger.info("🛠️ Checking for schema changes…")
        try:
            upgrade()
            app.logger.info("🛠️ Alembic reports DB is at head")
        except Exception:
            _stamp(revision='head')
            upgrade()
            app.logger.info("🛠️ Stamped and upgraded to head")
        try:
            _migrate(message="autogen", autogenerate=True)
            app.logger.info("🛠️ Autogenerated migration revision created")
        except Exception:
            app.logger.debug("🛠️ No new migrations detected")
        upgrade()
        app.logger.info("🛠️ Database fully migrated")

    # ─── Core routes & error‐handlers ─────────────────────────────────────────────
    from .errors import bp as errors_bp  # noqa: E402
    app.register_blueprint(errors_bp)
    from .routes import init_app as register_core_routes  # noqa: E402
    register_core_routes(app)

    # ─── JSON‐driven plugin loader with unbuffered prints ─────────────────────────
    print("🔌 Discovering plugins…", flush=True)
    plugins_dir    = os.path.join(project_root, 'plugins')
    loaded_plugins = []

    for name in sorted(os.listdir(plugins_dir)):
        manifest = os.path.join(plugins_dir, name, 'plugin.json')
        if not os.path.isfile(manifest):
            continue

        plugin_info = {
            'name': name,
            'models': [],
            'routes': None,
            'cli': [],
            'template_globals': [],
            'tasks': [],
            'tasks_init': [],
            'subplugins': []
        }
        errors = []

        try:
            meta = json.load(open(manifest))
        except Exception as e:
            print(f"  ✖ manifest load error for '{name}': {e}", flush=True)
            continue

        # 1) models
        for model_path in meta.get('models', []):
            try:
                importlib.import_module(model_path)
                plugin_info['models'].append(model_path)
            except Exception as e:
                errors.append(f"model '{model_path}': {e}")

        # 2) user_loader
        ul = meta.get('user_loader')
        if ul:
            try:
                m = importlib.import_module(ul['module'])
                fn = getattr(m, ul['callable'])
                fn(login_manager)
            except Exception as e:
                errors.append(f"user_loader '{ul}': {e}")

        # 3) routes
        rt = meta.get('routes')
        if rt:
            try:
                m      = importlib.import_module(rt['module'])
                bp_obj = getattr(m, rt['blueprint'])
                prefix = rt.get('url_prefix')
                app.register_blueprint(bp_obj, url_prefix=prefix, strict_slashes=False)
                plugin_info['routes'] = f"{rt['module']}::{rt['blueprint']}"
            except Exception as e:
                errors.append(f"routes '{rt}': {e}")

        # 4) CLI
        cli = meta.get('cli')
        if cli:
            try:
                m  = importlib.import_module(cli['module'])
                fn = getattr(m, cli['callable'])
                app.cli.add_command(fn)
                plugin_info['cli'].append(f"{cli['module']}::{cli['callable']}")
            except Exception as e:
                errors.append(f"cli '{cli}': {e}")

        # 5) template_globals
        for tg in meta.get('template_globals', []):
            try:
                mod_name, fn_name = tg['callable'].rsplit('.', 1)
                m = importlib.import_module(mod_name)
                fn = getattr(m, fn_name)
                app.jinja_env.globals[tg['name']] = fn
                plugin_info['template_globals'].append(tg['name'])
            except Exception as e:
                errors.append(f"template_global '{tg}': {e}")

        # 6) subplugins
        for sp in meta.get('subplugins', []):
            sub_info = {'name': sp['name'], 'models': [], 'routes': None}
            for mp in sp.get('models', []):
                try:
                    importlib.import_module(mp)
                    sub_info['models'].append(mp)
                except Exception as e:
                    errors.append(f"subplugin model '{mp}': {e}")
            srt = sp.get('routes')
            if srt:
                try:
                    m      = importlib.import_module(srt['module'])
                    bp_obj = getattr(m, srt['blueprint'])
                    app.register_blueprint(bp_obj, url_prefix=srt.get('url_prefix'), strict_slashes=False)
                    sub_info['routes'] = f"{srt['module']}::{srt['blueprint']}"
                except Exception as e:
                    errors.append(f"subplugin routes '{srt}': {e}")
            plugin_info['subplugins'].append(sub_info)

        # 7) tasks
        for task_mod in meta.get('tasks', []):
            try:
                importlib.import_module(task_mod)
                plugin_info['tasks'].append(task_mod)
            except Exception as e:
                errors.append(f"task '{task_mod}': {e}")

        # 8) tasks_init
        for hook in meta.get('tasks_init', []):
            try:
                m       = importlib.import_module(hook['module'])
                fn      = getattr(m, hook['callable'])
                fn(app)
                plugin_info['tasks_init'].append(f"{hook['module']}::{hook['callable']}")
            except Exception as e:
                errors.append(f"tasks_init '{hook}': {e}")

        if errors:
            print(f"  ✖ Plugin '{name}' errors: " + "; ".join(errors), flush=True)
        else:
            print(f"  ✔ Plugin '{name}' loaded", flush=True)
            loaded_plugins.append(plugin_info)

    # summary
    print("🌟 Loaded plugins summary:", flush=True)
    for info in loaded_plugins:
        print(
            f"  • {info['name']}: "
            f"models={info['models']}, "
            f"routes={info['routes']}, "
            f"cli={info['cli']}, "
            f"template_globals={info['template_globals']}, "
            f"tasks={info['tasks']}, "
            f"tasks_init={info['tasks_init']}, "
            f"subplugins={[s['name'] for s in info['subplugins']]}",
            flush=True
        )

    # ─── Context processors, before/after request, teardown ─────────────────────
    @app.context_processor
    def inject_current_year():
        return {'current_year': datetime.now().year}

    @app.context_processor
    def inject_now():
        return {'utcnow': datetime.utcnow()}

    @app.before_request
    def start_timer():
        request._start_time = time.time()

    @app.after_request
    def log_analytics(response):
        from plugins.admin.models import AnalyticsEvent  # noqa: E402
        try:
            duration = time.time() - getattr(request, '_start_time', time.time())
            ev = AnalyticsEvent(
                method          = request.method,
                path            = request.path,
                status_code     = response.status_code,
                response_time   = duration,
                user_agent      = request.headers.get('User-Agent'),
                referer         = request.headers.get('Referer'),
                accept_language = request.headers.get('Accept-Language'),
            )
            db.session.add(ev)
            db.session.commit()
        except Exception:
            db.session.rollback()
        return response

    @app.teardown_appcontext
    def shutdown_session(exception=None):
        db.session.remove()

    print("✅ create_app() complete; ready to serve", flush=True)
    return app
