import os
import datetime
import logging
from logging.handlers import RotatingFileHandler

from flask import Flask, render_template
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.assets import Environment, Bundle
from flask.ext.restless import APIManager
from flask_admin import Admin

from rfs.config import DefaultConfig

db = SQLAlchemy()
assets = Environment()

from rfs.products.views import products
from rfs.orders.views import orders

DEFAULT_BLUEPRINTS = (
    products, orders
)

from rfs.products.models import Product, ProductVariant
from rfs.orders.models import Order

DEFAULT_API_MODELS = (
    Product,
    ProductVariant,
    Order
)


from rfs.products.admin import ProductAdmin

DEFAULT_ADMIN_MODELS = (
    ProductAdmin,
)
    

def create_app(config=None, app_name=None, blueprints=None,api_models=None,admin_models=None):
    """
    Create and configure the Flask app
    """
    if app_name is None:
        app_name = DefaultConfig.PROJECT
    if blueprints is None:
        blueprints = DEFAULT_BLUEPRINTS
    if api_models is None:
        api_models = DEFAULT_API_MODELS
    if admin_models is None:
        admin_models = DEFAULT_ADMIN_MODELS
    app = Flask(app_name,
                static_url_path='/static',
                instance_relative_config=True
               )

    configure_app(app, config)
    configure_template_processors(app)
    configure_blueprints(app, blueprints)
    configure_logging(app)
    configure_error_handlers(app)
    configure_database(app)
    configure_assets(app)
    configure_api(app,api_models)
    configure_admin(app,admin_models)

    return app

def configure_app(app, config=None):
    # http://flask.pocoo.org/docs/api/#configuration
    app.config.from_object(DefaultConfig)

    if config is not None:
        app.config.from_object(config)

def configure_logging(app):

    if app.debug or app.testing:
        app.logger.setLevel(logging.DEBUG)
        # Skip debug and test mode. Just check standard output.
        return

    app.logger.setLevel(logging.INFO)

    info_log = os.path.join(app.config['LOG_FOLDER'], 'shop.log')
    info_file_handler = RotatingFileHandler(info_log, maxBytes=100000, backupCount=10)
    info_file_handler.setLevel(logging.INFO)
    info_file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s '
        '[in %(pathname)s:%(lineno)d]')
    )
    app.logger.addHandler(info_file_handler)


def configure_template_processors(app):
    @app.context_processor
    def my_processors():
        def now(format="%d-%m-%d %H:%M:%S"):
            return datetime.datetime.now().strftime(format)

        return dict(now=now)

def configure_blueprints(app, blueprints):
    for blueprint in blueprints:
        app.register_blueprint(blueprint)

def configure_error_handlers(app):

    @app.errorhandler(403)
    @app.errorhandler(401)
    def forbidden_page(error):
        return render_template("errors/403.html"), 403

    @app.errorhandler(404)
    def page_not_found(error):
        return render_template("errors/404.html"), 404

    @app.errorhandler(500)
    def server_error_page(error):
        return render_template("errors/500.html"), 500

def configure_database(app):
    db.init_app(app)

def configure_assets(app):
    assets.init_app(app)
    js = Bundle("libs/react/react.js",
                "libs/jquery/dist/jquery.js",
                "libs/fluxxor/build/fluxxor.js",
                filters="jsmin", output="libs/bundle.js")
    assets.register("js_all",js)

    import rfs.webassets_filters
    jsx = Bundle(
        "products/jsx/product.js",
        "orders/jsx/cart.js",
        "jsx/app.js",
        filters="jsx", output="js/app.js")
    assets.register("jsx_all",jsx)
    
    css = Bundle("css/app.css",
                 filters="cssmin",
                 output="libs/bundle.css")
    assets.register("css_all",css)

def configure_api(app, api_models):
    api_manager=APIManager(app,flask_sqlalchemy_db=db)
    for api in api_models: 
        api_manager.create_api(api)


def configure_admin(app,admin_models):
    admin = Admin(app,name="Store backend")
    for admin_view in admin_models:
        admin.add_view(admin_view(Product,db.session))