# Source code of the Ubuntu-it website
# Copyright (C) 2015 Pietro Albini <pietroalbini@ubuntu.com>
# Copyright (C) 2023 shadMod <m.allegro@shadmod.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import sys

import flask
import shutil

from . import actions
from . import auth
from . import cache
from . import db
from . import navbar
from . import pages
from . import redirects
from . import utils
from .constants import BASE_DIR

from .src.download.views import Downloads
from .src.news.views import News


def create_app(data_path, debug=False):
    """Create a new instance of the application"""
    # Normalize the data path
    data_path = os.path.expanduser(os.path.abspath(data_path))

    app = flask.Flask(
        __name__,
        static_url_path="/+assets",
        instance_path=data_path,
    )

    # Prepare the data directory
    init_data_directory(data_path, debug)

    # Load the secret key
    with open(os.path.join(data_path, "secret_key")) as fn:
        app.secret_key = fn.read().strip()

    # Initialize the database
    app.db = db.Database(os.path.join(data_path, "database.db"))
    app.db.init()

    app.config["CACHE_PATH"] = os.path.join(data_path, "cache")
    cache.install_cache(app)

    utils.prepare_app(app)

    app.news = News(debug)
    app.download = Downloads(data_path)

    app.register_blueprint(redirects.prepare_blueprint(app))
    app.register_blueprint(app.news.prepare_blueprint(app), url_prefix="/news")
    app.register_blueprint(app.download.prepare_blueprint(app), url_prefix="/download")
    app.register_blueprint(actions.prepare_blueprint(app), url_prefix="/+actions")
    app.register_blueprint(auth.prepare_blueprint(app), url_prefix="/+auth")
    app.register_blueprint(pages.prepare_blueprint(app))

    nav = navbar.Navbar()
    nav.add_generator("download-distros", app.download.generate_navbar)
    nav.install(app)

    @app.errorhandler(403)
    def forbidden(error):
        return flask.render_template("403.html"), 403

    @app.errorhandler(404)
    def not_found(error):
        return flask.render_template("404.html"), 404

    return app


def init_data_directory(data_path: str):
    """Initialize the data directory"""
    src_directory = os.path.dirname(os.path.abspath(__file__))

    # Create all the directories
    dirs = ["", "cache"]
    for _dir in dirs:
        os.makedirs(os.path.join(data_path, _dir), exist_ok=True)

    # Copy news tree dirs and file
    ROOT_NEWS = os.path.join(BASE_DIR, "assets/news")
    NEWS_PATH = os.path.join(data_path, "news")
    if sys.version_info[:2] > (3, 8):
        shutil.copytree(ROOT_NEWS, NEWS_PATH, dirs_exist_ok=True)
    else:
        try:
            # if path already exists, remove it before copying with copytree()
            if os.path.exists(NEWS_PATH):
                shutil.rmtree(NEWS_PATH)
                shutil.copytree(ROOT_NEWS, NEWS_PATH)
            else:
                shutil.copytree(ROOT_NEWS, NEWS_PATH)
        except OSError as ex:
            shutil.copy(ROOT_NEWS, NEWS_PATH)
        except Exception as ex:
            print(ex)

    # Initialize the cache
    static_dirs = {"static": "+assets"}
    for src, dest in static_dirs.items():
        dest = os.path.join(data_path, "cache", dest)

        # Create the correct symlink
        if os.path.exists(dest):
            os.remove(dest)
        os.symlink(os.path.join(src_directory, src), dest)

    # Generate the secret key if not present
    secret_key_path = os.path.join(data_path, "secret_key")
    if not os.path.exists(secret_key_path):
        with open(secret_key_path, "w") as f:
            f.write("%s\n" % utils.random_key(64))
        os.chmod(secret_key_path, 0o400)


def init_data_downloads(data_path, debug=False):
    # Initialize the download files
    _download = Downloads(data_path, debug=debug)
    if debug is False:
        # mk cache file
        _download.store_cache_file()
