from app import app
from flask import render_template, url_for, request, flash, redirect, session,  make_response, send_from_directory
import json
from markupsafe import escape
from werkzeug.utils import secure_filename
import os
import re
from app.forms import ImageForm, ImageUpload
from app.models.sites import DataSite
from app.models.blocs import MyBlocs
from PIL import Image
from flask_bcrypt import Bcrypt
from copy import deepcopy
from app.forms import Login, CreateWebsite
from functools import wraps
from app.utils import login_required, list_type_blocs, list_type_pages
from flask_wtf.csrf import CSRFProtect
from datetime import datetime


csrf = CSRFProtect(app)

app.secret_key = 'your_secret_key'
app.config.from_pyfile('config.py')

THEMES_FOLDER = '/themes/'
USERS_FOLDER = app.config['USERS_FOLDER']
bcrypt = Bcrypt(app)

EXT_IMG = {'.jpg', '.JPG', '.png', '.PNG', '.gif', '.GIF', '.bmp', '.BMP', '.jpeg', '.JPEG' }


@app.route('/login', methods=['GET', 'POST'])
def login():
    form = Login()
    if 'username' in session :
        resp = redirect(url_for('edit', _external=True))
    else :
        resp = redirect(url_for('login', _external=True))
       
        if request.method == 'POST' :
            login = str(request.form['login'])
            password = str(request.form['passwd'])
            if login in os.listdir(USERS_FOLDER):
               db = DataSite(login, USERS_FOLDER)
               db.load_file()
               passwd_bcrypt = db.datas['config']['passwd']
            
               if bcrypt.check_password_hash(passwd_bcrypt, password) is True:
                   session['username'] = login
                   resp =  redirect(url_for('edit', _external=True))
               else:
                   flash(u'Mauvais mot de passe', 'error')
                    
        else:
            resp = render_template('/admin/login.html', form=form)

    return resp

@app.route( '/logout', methods=['GET'] )
@login_required
def logout():
    session.pop('username', None) # Supprimer username de la session s'il s'y trouve
    session.clear()
    return redirect(url_for('login'))


@app.route ('/new-website', methods=[ 'GET' ])
def new_website():
    form = CreateWebsite()
    return render_template('/admin/create_website.html', form=form)

@app.route('/create-website', methods=[ 'POST'])
def create_website():
    form = CreateWebsite(request.form)
    login = str(form.login.data)
    passwd = str(form.passwd.data)
    theme = str(form.theme.data)
    if form.validate():
        db = DataSite(login, USERS_FOLDER)
        
        if db.create(theme):
            db.set_passwd(bcrypt.generate_password_hash(passwd))
            session['username'] = login
            resp = redirect(url_for('edit'))
        else:
            flash(u'Erreur lors de la création du site', error)
            resp = redirect(url_for('login', _external=True))
    else:
        resp = render_template('/admin/create_website.html', form=form)
    return resp

@app.route('/', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/', methods=['GET'])
@login_required
def edit(page):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    db = DataSite(user, USERS_FOLDER)
    db.load_file()
    type_pages = list_type_pages(db.datas['config']['theme'])
    type_blocs = list_type_blocs(db.datas['config']['theme'])
    myblocs = db.get_blocs(page)
    footer = db.get_footer()
    return render_template('/edit.html.tpl',
                           blocEdit="",
                           config=db.datas['config'],
                           menu=db.datas['menu'],
                           footer=footer,
                           type_blocs=type_blocs,
                           type_pages=type_pages,
                           theme=db.datas['config']['theme'],
                           pagesList=db.get_pages(),
                           page=page,
                           myblocs=myblocs,
                           serverInfos=db.datas['publish']
                          )


@app.route('/edit/images/<bloc>/<image>', methods=['GET', 'POST'], defaults={'page': ''})
@app.route('/<page>/edit/images/<bloc>/<image>', methods=['GET', 'POST'])
@login_required
def edit_img(page, image, bloc):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    image = '%s' % escape(image)
    bloc = '%s' % escape(bloc)

    db = DataSite(user, USERS_FOLDER)
    db.load_file()
    theme = db.datas['config']['theme']
    form = ImageForm()
    myblocs = db.get_blocs(page)

    if form.validate_on_submit():
        if request.method == 'POST':
            myblocs[bloc][image] = db.update_img(image, form)
            db.save_blocs(page, myblocs)
            form = MyBlocs.make_form()
            if "hx-request" in request.headers:
                return make_response(('Image modified', 200))
            else:
                return redirect ('/'+page+'#'+bloc)
    else:
        return render_template(THEMES_FOLDER+theme+'/blocs/edit/_image-edit.html', form=form, page=page, image=myblocs[bloc][image], bloc=myblocs[bloc])

@app.route('/upload-gallery/<bloc>', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/upload-gallery/<bloc>', methods=['POST'])
@login_required
def drop_upload(page, bloc):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    datas = dict()
    file = request.files['file']
    filename = secure_filename(file.filename)
    myblocs = db.get_blocs(page)['blocs']
    ext = os.path.splitext(filename)[-1]
    save_path = os.path.join(USERS_FOLDER, user, 'public', 'img' )
    if ext not in EXT_IMG :
        return make_response(('Le fichier n\'est pas une image', 400))

    current_chunk = int(request.form['dzchunkindex'])
    
    if (os.path.isfile(os.path.join(save_path, filename ))) and  current_chunk == 0:
        return make_response(('Un fichier avec le même nom existe déjà', 400))

    try:
        with open(os.path.join(save_path, filename ), 'ab') as f:
            f.seek(int(request.form['dzchunkbyteoffset']))
            f.write(file.stream.read())
    except OSError:
        return make_response(("Une erreur est survenue,"
                              " Impossible d'écrire le fichier sur le disque", 500))
    total_chunks = int(request.form['dztotalchunkcount'])

    if current_chunk + 1 == total_chunks:
        # This was the last chunk, the file should be complete and the size we expect
        if os.path.getsize(os.path.join(save_path, filename )) != int(request.form['dztotalfilesize']):
            return make_response(('La taille du fichier source est différentes', 500))
        else:
            with Image.open(os.path.join(save_path, filename )) as img :
                img.thumbnail((640,480))
                img.save(os.path.join(save_path, 'thumbnails', filename ) )
    
    myblocs[bloc][filename] = db.add_img(filename)
    db.save_blocs(page, myblocs)
    return make_response(('Chunk upload succesfull', 200))


@app.route('/edit/images/<bloc>/del/<image>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/edit/images/<bloc>/del/<image>', methods=['GET'])
@login_required
def del_image(page, bloc, image):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    myblocs = db.get_blocs(page)['blocs']

    myblocs[bloc] = db.del_img(myblocs[bloc], image)
    db.save_blocs(page, myblocs)
    
    return make_response(('Image removed', 200))

@app.route('/imgs_list/<blocname>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/imgs_list/<blocname>', methods=['GET'])
@login_required
def images_list(page, blocname):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    blocname = '%s' % escape(blocname) 

    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    theme = db.datas['config']['theme']
    myblocs = db.get_blocs(page)
    bloc = myblocs[blocname]
    
    return render_template(THEMES_FOLDER+theme+'/blocs/edit/images_list.html',page=page,
                           bloc=bloc)

@app.route('/edit/<blocname>', methods=['GET', 'POST'], defaults={'page': ''})
@app.route('/<page>/edit/<blocname>', methods=['GET', 'POST'])
@login_required
def edit_bloc(page, blocname):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    theme = db.datas['config']['theme']
    myblocs = db.get_blocs(page)
    bloc = myblocs[blocname]
    form = MyBlocs.make_form()
      
    if request.method == 'POST':
        datas=dict()
        if form.validate_on_submit():
            
            for field, value in request.form.items():
                if field != 'csrf_token' and field != 'image':
                    myblocs[blocname][field] = value
            if 'image' in request.files.keys():
                file = request.files['image']
                filename = secure_filename(file.filename)
                bg_custom = 'bg_custom_'+bloc['name']+filename
                file.save(os.path.join(USERS_FOLDER, user, 'public', 'img', bg_custom ))
                if 'bg_img' in bloc.keys():
                    db.del_img(bloc, bloc['bg_img'])

                    
                myblocs[blocname]['bg_img'] = bg_custom
                
            db.save_blocs(page, myblocs)
            flash(u'Vos changements on été pris en compte et enregistré', 'success')
        else:
            print(form.errors)

        if "hx-request"  in request.headers:
            resp = render_template(THEMES_FOLDER+theme+'/blocs/'+bloc['type']+'.html',
                                   theme=theme,
                                   page=page,
                                   bloc=myblocs[blocname])
        else:
            resp = redirect(url_for('edit', page=page))
        
        
    else:
        if "hx-request"  in request.headers:
                resp = render_template(THEMES_FOLDER+theme+'/blocs/edit/'+bloc['type']+'.html',
                                   theme=theme,
                                   bloc=bloc,
                                   page=page,
                                   form=form)

        else:
            resp = render_template('/edit.html.tpl',
                                   config=db.datas['config'],
                                   menu=db.datas['menu'],
                                   myblocs=myblocs,
                                   page=page,
                                   theme=theme,
                                   blocEdit=bloc['name'],
                                   form=form)
    return resp

@app.route('/view/<bloc>', methods=['GET'], defaults={'page': ''})
@app.route('/<page>/view/<bloc>', methods=['GET'])
@login_required
def view_bloc(page, bloc):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)

    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    theme = db.datas['config']['theme']
    myblocs = db.get_blocs(page)
    bloc = myblocs[bloc]
    
    return render_template(THEMES_FOLDER+theme+'/blocs/'+bloc['type']+'.html',
                           bloc=bloc)

@app.route('/addBloc', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/addBloc', methods=['POST'])
@login_required
def add_bloc(page):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    datas = dict()
    print("page add bloc :" + page)
    myblocs = db.get_blocs(page)['blocs']

    blocName = '%s' % escape(request.form['blocName'])
    blocType = request.form['blocType']
    bloc = MyBlocs.new(blocName, blocType, db.datas['config']['theme'])
    test_name = "^[A-Za-z0-9_-]*$"

    if bloc != None:
        if bool(re.match(test_name, blocName)) :

            myblocs[blocName] = { 'name': blocName, 'type': blocType}
            flash (u'Bloc ajouté avec succès', 'succes');
        else:
            return make_response(('Invalid bloc Name', 500))
  
    db.save_blocs(page, myblocs)

    if page == '':
        return redirect("/#"+blocName)
    else:
        return redirect("/"+page+"#"+blocName)

@app.route('/addPage', methods=['POST'])
@login_required
def add_page():
    user = '%s'% escape(session['username'])
    
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    datas = dict()

    pageName = '%s' % escape(request.form['pageName'])
    pageType = request.form['pageType']
    test_name = "^[A-Za-z0-9_-]*$"
    if bool(re.match(test_name, pageName)) :
        try :
            page = db.new_page(pageName, pageType)
        except FileExistsError:
            return make_response(('Page already exist', 500))
        else:
            flash (u'Page créé avec succès', 'success');
    else:
         return make_response(('Invalid type ', 500))
  
    
    mypages = db.get_pages()
    return redirect(url_for('edit', page=pageName))

@app.route('/rmPage/<pageName>')
@login_required
def rm_page(pageName):
    pageName = '%s' % escape(pageName)
    user = '%s'% escape(session['username'])
    
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    try:
        db.rm_page(pageName)
        flash (u'Page supprimé avec succès', 'success');
    except OSError as e:
        flash (u"La page n'a pa pu être supprimer", 'error');


    return redirect(url_for('edit'))
    
@app.route('/sortblocs', methods=['POST'], defaults={'page': ''})
@app.route('/<page>/sortblocs', methods=['POST'])
@login_required
def sortblocs(page):
    user = '%s'% escape(session['username'])
    page = '%s' % escape(page)
    
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    config = db.datas['config']
    menu = db.datas['menu']
    datas = dict()
    myblocs = dict()
    
    myblocs_orig = db.get_blocs(page)['blocs']
    
    for bloc in request.form:
        if bloc != 'csrf_token':
            bloc_name = bloc.replace("bloc-", "", 2)
            myblocs[bloc_name] = myblocs_orig[bloc_name]
            flash (u'Les blocs ont été réagencés', 'succes');
    
    db.save_blocs(page, myblocs)
    
    return redirect(url_for('edit', page=page))

@app.route('/editmenu', methods=['POST'])
@login_required
def edit_menu():
    user = '%s'% escape(session['username'])
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    db.datas['menu']=dict()
    for menu in request.form:
        if '§§' in menu:
            submenu = menu.split("§§")
            if submenu[0] in db.datas['menu'].keys():
                db.datas['menu'][submenu[0]] = db.datas['menu'][submenu[0]] | {submenu[1]: str(request.form[menu])}
            else:
                db.datas['menu'][submenu[0]] = {submenu[1]: str(request.form[menu])}
        
        elif menu == 'csrf_token':
            pass
        else:
            db.datas['menu'][menu] = str(request.form[menu])
            
    db.save_menu(db.datas['menu'])
    flash (u'Le menu a bien été modifier', 'success');
    return redirect(url_for('edit'))
 
@app.route('/img/<filename>')
@login_required
def myimgs(filename):
   user = '%s' % escape(session['username'])
   return send_from_directory(
       os.path.join(USERS_FOLDER, user, 'public', 'img'), filename )

@app.route('/img/thumbnails/<filename>')
@login_required
def mythumbnails(filename):
   user = '%s' % escape(session['username'])
   return send_from_directory(
       os.path.join(USERS_FOLDER, user, 'public', 'img', 'thumbnails'), filename )

@app.route('/favicon.ico', methods=['GET'])
@app.route('/favicon.ico/', methods=['GET'])
@login_required
def favicon():
    return make_response(('Ok! ', 200))


@app.route('/publish_progress/', methods=['GET'])
@login_required
def publish_progress():
    user = '%s' % escape(session['username'])
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    progress = 0 
    
    error = False
    completed = False
    status = db.get_status_publish()
    
    match status:
        case "Generated" :
            progress = 30
        case "Login Incorrect" | "Upload error" :
            progress = 100
            error = True
        case "Identification verified":
            progress = 50
            status = "Upload ..."
        case "Uploaded":
            progress = 80
            status = "Set permissions ..."
        case str(x) if "Uploaded at:" in x:
            progress = 100
            completed = True
            status = status.replace("Uploaded at:", "")

    return render_template('admin/_publish-progress.html', progress=progress,
                                                           status=status,
                                                           error = error,
                                                           completed = completed)



@app.route('/publish', methods=['POST'])
@login_required
def publish():
    user = '%s' % escape(session['username'])
    db = DataSite(user, USERS_FOLDER)
    db.load_file()

    listPages = db.get_pages()
    theme = db.datas['config']['theme']
    menu = db.datas['menu']
    test_alphanum = "^[A-Za-z0-9/_-]*$"
    

    password = str(request.form['password'])

    serverInfos = dict()
    serverInfos['Address'] = str(request.form['serverAddress'])
    serverInfos['Port'] = str(request.form['serverPort'])
    serverInfos['username'] = str(request.form['username'])
    serverInfos['folder'] = str(request.form['folder'])
    footer = db.get_footer()
    for page in listPages: 
        myblocs = db.get_blocs(page)
        htmlExport = render_template('/index.html.tpl',
                           config=db.datas['config'],
                           menu=menu,
                           footer=footer,
                           theme=theme,
                           page=page,
                           myblocs=myblocs)
        db.writeHTML(theme, page, htmlExport)


    # Export index.html landing page
    myblocs = db.get_blocs('')
    htmlExport = render_template('/index.html.tpl',
                           config=db.datas['config'],
                           menu=menu,
                           footer=footer,
                           theme=theme,
                           page='/',
                           myblocs=myblocs)

    db.writeHTML(theme, '/', htmlExport)
    db.copy_static_files()
    db.update_status_publish("Generated")
    

    if serverInfos['Port'] == '' or serverInfos['Port'].isdigit() == False :
        serverPort = '22' 



    if serverInfos['folder'] == '' :
        folder = '/'
    elif(bool(re.match(test_alphanum, serverInfos['folder'])) == False):
        serverInfos['folder'] = '/'

    
    if db.test_server_login(serverInfos, password ) == False:
        db.update_status_publish ("Identification Incorrect")
        return make_response(('Login Incorrect', 500))
    else:
        db.update_status_publish("Identification verified")
        if db.publish(serverInfos, password) == False:
            db.update_status_publish ("Upload error")
            return make_response('Upload Error !')
    
    #Generate date of success upload
    now = datetime.now()
    date_upload = now.strftime("%d/%m/%Y, %H:%M:%S") 
    db.update_status_publish ("Uploaded at: "+date_upload) 
    db.save_server_info(serverInfos) 
    
    return redirect(url_for('edit'))



