import os
from pathlib import Path

from flask import Flask, redirect, send_from_directory
from flask_cors import CORS

from app.config import Config
from app.extensions import db, bcrypt, migrate


def _auto_migrate(db):
    """Add missing columns to existing tables by comparing model definitions
    with actual database schema. This handles the case where new columns are
    added to models but db.create_all() only creates new tables."""
    import sqlalchemy

    with db.engine.connect() as conn:
        inspector = sqlalchemy.inspect(db.engine)

        for table_name, table in db.metadata.tables.items():
            if not inspector.has_table(table_name):
                continue

            existing_cols = {col['name'] for col in inspector.get_columns(table_name)}

            for column in table.columns:
                if column.name not in existing_cols:
                    # Determine SQL type
                    col_type = column.type.compile(db.engine.dialect)

                    # Determine default value
                    default = ''
                    if column.default is not None:
                        if hasattr(column.default, 'arg'):
                            val = column.default.arg
                            if callable(val):
                                default = ''
                            elif isinstance(val, str):
                                default = f" DEFAULT '{val}'"
                            elif isinstance(val, bool):
                                default = f" DEFAULT {1 if val else 0}"
                            elif isinstance(val, (int, float)):
                                default = f" DEFAULT {val}"
                    elif column.nullable:
                        default = ' DEFAULT NULL'
                    elif col_type.upper().startswith(('VARCHAR', 'TEXT')):
                        default = " DEFAULT ''"
                    elif col_type.upper().startswith(('INTEGER', 'BIGINT', 'FLOAT')):
                        default = " DEFAULT 0"
                    elif col_type.upper() == 'BOOLEAN':
                        default = " DEFAULT 0"

                    sql = f'ALTER TABLE "{table_name}" ADD COLUMN "{column.name}" {col_type}{default}'
                    try:
                        conn.execute(db.text(sql))
                        print(f'[Auto-Migrate] Added column {table_name}.{column.name} ({col_type})')
                    except Exception as e:
                        print(f'[Auto-Migrate] Failed to add {table_name}.{column.name}: {e}')

        conn.commit()


def create_app(config_class=Config):
    # Check if static frontend build exists (Docker production mode)
    static_dir = Path(__file__).resolve().parent.parent / 'static'
    if static_dir.exists():
        app = Flask(__name__, static_folder=str(static_dir), static_url_path='')
    else:
        app = Flask(__name__)

    app.config.from_object(config_class)

    # Ensure data directories exist
    Path(app.config['UPLOAD_PATH']).mkdir(parents=True, exist_ok=True)
    db_dir = Path(app.config['SQLALCHEMY_DATABASE_URI'].replace('sqlite:///', '')).parent
    db_dir.mkdir(parents=True, exist_ok=True)

    # Initialize extensions
    db.init_app(app)
    bcrypt.init_app(app)
    migrate.init_app(app, db)

    # CORS
    CORS(app, resources={r'/api/*': {'origins': app.config['FRONTEND_URL']}},
         supports_credentials=True)

    # Register blueprints
    from app.api import api_bp
    app.register_blueprint(api_bp)

    # Well-known URLs for CalDAV/CardDAV auto-discovery (iOS, DAVx5, etc.)
    @app.route('/.well-known/caldav')
    def wellknown_caldav():
        return redirect('/dav/', code=301)

    @app.route('/.well-known/carddav')
    def wellknown_carddav():
        return redirect('/dav/', code=301)

    # iCal export (public, no auth)
    @app.route('/ical/<token>')
    def ical_export_route(token):
        from app.api.calendar import ical_export as _ical_export
        return _ical_export(token)

    # Serve frontend SPA for all non-API routes (production/Docker)
    if static_dir.exists():
        @app.route('/')
        def serve_index():
            return send_from_directory(str(static_dir), 'index.html')

        @app.errorhandler(404)
        def serve_spa(e):
            return send_from_directory(str(static_dir), 'index.html')

    # Create tables + auto-migrate missing columns
    with app.app_context():
        from app import models  # noqa: F401
        db.create_all()

        # Auto-migrate: add missing columns to existing tables
        _auto_migrate(db)

        # Enable WAL mode for SQLite
        with db.engine.connect() as conn:
            conn.execute(db.text('PRAGMA journal_mode=WAL'))
            conn.commit()

    # Start backup scheduler (only in main process, not reloader)
    if not app.debug or os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
        from app.services.backup_scheduler import start_backup_scheduler
        start_backup_scheduler(app)

    return app
