import zipfile
from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for, session, jsonify
import os
import shutil
from flask import request
from .k2cfg import k2
from .k2comp import Component
from flask_migrate import Migrate
import requests
import subprocess
from flask import current_app
import yaml
from functools import wraps
import os
import sys
from .k2admmenu import K2admin_menus,  K2admin_Menus_Prava
from sqlalchemy import text
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_login import login_user, current_user,  logout_user, login_required
import platform


# initialize db
# migrate = Migrate(app, db)

components_bp = Blueprint('components', __name__, template_folder='templates')
config = yaml.safe_load(open("db/first-login.yml"))

db = k2.db


def first_login_required(view_func):
    @wraps(view_func)
    def decorated_view(*args, **kwargs):
        if 'username' not in session:
            return redirect(url_for('components.first_login'))
        return view_func(*args, **kwargs)

    return decorated_view

# def append_to_yaml_file(file_path, data):
#    with open(file_path, 'r') as f:
#        existing_data = yaml.safe_load(f) or []
#
#    if not existing_data:
#        start_id = 1
#    else:
#        start_id = max(item.get('id', 0) for item in existing_data) + 1
#
#    for i, item in enumerate(data, start=start_id):
#        item['id'] = i
#        existing_data.append(item)
#
#    with open(file_path, 'w') as f:
#        yaml.dump(existing_data, f, default_flow_style=False)

def remove_from_yaml_file(file_path, component_name):
    with open(file_path, 'r') as f:
        existing_data = yaml.safe_load(f) or []

    updated_data = [item for item in existing_data if item.get('component_name') != component_name]

    with open(file_path, 'w') as f:
        yaml.dump(updated_data, f, default_flow_style=False)


@components_bp.route('/api/languages', methods=['GET'])
def find_language():
    components = Component.query.all()
    languages_paths = []
    # Додаємо головну директорію languages
    languages_paths.append('languages')
    # Знаходимо шляхи до директорій languages всередині папки components
    for component in components:
        component_languages_directory = 'components/' + component.name + '/' + component.name + '/languages'
        languages_paths.append(component_languages_directory)
    result = ';'.join(languages_paths)
    print(result)
    return result


@components_bp.route('/', methods=['GET', 'POST'])
def home():
    components = Component.query.all()
    components_names = [component.name for component in components]
    if 'k2site' in components_names:
        return redirect(url_for('components.component_add_dasb')) #змінити на ендпоінт головної k2site
    else:
        return redirect('/dashboard')



@components_bp.route('/first-login', methods=['GET', 'POST'])
def first_login():
    if request.method == "POST":
        username = request.form.get("username")
        password = request.form.get("password")
        for user in config["users"]:
            if user["username"] == username and user["password"] == password:
                session['username'] = username  # Збереження ім'я користувача в сесії
                return redirect(url_for('components.dashboard'))
        return "Невірне ім'я користувача або пароль."
    return render_template('first-login.html')


@components_bp.route('/change_language/<lang>')
def change_language(lang):
    session['lang'] = lang
    k2.current_language = lang
    return redirect(request.referrer)


@components_bp.route('/dashboard')
def dashboard():
    components = Component.query.all()
    components_names = []
    components_names = [component.name for component in components]
    if 'k2site' in components_names:
        return redirect(url_for('components.home')) 
    else:
        # Компоненти доступні для встановлення
            # GET-запит до API
        try:
            response = requests.get(f'{k2.update_domain}api/components')
            json_data = response.json()
            # Перетворення JSON-об'єкту на масив
            component_server = [item for item in json_data]
        except:
            component_server = None

        # Встановлені компоненти
        components = Component.query.all()
        components_names = [component.name for component in components]
        # Отримання значення пошукового запиту з параметрів URL
        search_query = request.args.get('search_query')
        # print(search_query)
        filtered_components = []
        if search_query:
            filtered_components = [component for component in component_server if
                                   (search_query.lower() in component['name'].lower() if component['name'] else False) or
                                   (search_query.lower() in component['description'].lower() if component[
                                       'description'] else False)]
        else:
            filtered_components = component_server
        current_language = k2.current_language

        return render_template('dashboard.html', components=components,
                               components_names=components_names, component_server=filtered_components,
                               search_query=search_query, language=current_language)


@components_bp.route('/show_components/<string:component_id>')
def show_components(component_id):
    response = requests.get(f'{k2.update_domain}api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    return render_template('component-info.html', selected_component=selected_component)


@components_bp.route('/show_components-site/<string:component_id>')
def show_components_site(component_id):
    response = requests.get(f'{k2.update_domain}api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    return render_template('component-info-site.html', selected_component=selected_component)


@components_bp.route('/install_components_git/<string:component_id>')
def install_components_git(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    # component = Component.get_repository_by_id(component_id)
    response = requests.get(f'{k2.update_domain}api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    # Назва репозиторія
    repository_name = selected_component['name']
    # Шлях до папки репозиторія в межах проекту
    repository_folder = os.path.join(project_folder, repository_name)
    # URL репозиторія

    git_repo_url = selected_component['git_link']
    try:
        # Перевірка наявності папки репозиторія
        if not os.path.exists(repository_folder):
            # Створення папки репозиторія
            os.makedirs(repository_folder)
        # Шлях до файлу __init__.py
        init_file = os.path.join(repository_folder, '__init__.py')
        # Перевірка наявності файлу __init__.py
        if not os.path.exists(init_file):
            # Створення пустого файлу __init__.py
            open(init_file, 'a').close()
        # підключення до приватного репозиторію
        # os.environ['GITLAB_PRIVATE_TOKEN'] = '8xxTpxrKVDGoXD5ynjiW'
        # git_repo_url_with_token = git_repo_url.replace('https://',
        #                                               f'https://oauth2:{os.environ["GITLAB_PRIVATE_TOKEN"]}@')

        # Команда для встановлення з використанням git_repo_url і повного шляху до папки репозиторія
        command = ['venv/Scripts/python.exe', '-m', 'pip', 'install', '--use-pep517', 'git+' + git_repo_url,
                   '--target=' + repository_folder]
        # Виконуємо команду встановлення
        subprocess.check_call(command)

        # Шлях до файлу, до якого потрібно додати код
        file_path = 'routes.py'
        # Код, який потрібно додати
        code = selected_component['dependencies']
        # Відкриття файлу у режимі дозапису
        with open(file_path, 'a') as file:
            # Запис нового коду у файл
            file.write('\n')
            file.write(code)
            file.write('\n')
        component = Component.query.filter_by(name=selected_component['name']).first()
        if not component:
            new_component = Component(
                name=selected_component['name'],
                description=selected_component['description'],
                version=selected_component['version'],
                git_link=selected_component['git_link'],
                dependencies=selected_component['dependencies'],
                installed=True
            )
            # Add the new component to the database
            db.session.add(new_component)
            db.session.commit()

        # Повертаємо повідомлення про успішне встановлення
        return f'Installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    except subprocess.CalledProcessError as e:
        return 'Error installing : ' + str(e)


@components_bp.route('/install_components/<string:component_id>')
def install_component_from_archive(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    # Отримати відповідну компоненту зі списку компонент
    response = requests.get(f'{k2.update_domain}api/components')
    json_data = response.json()
    selected_component = next((component for component in json_data if component['id'] == component_id), None)
    if selected_component is None:
        return 'Component not found'
    # Отримати посилання на архів компоненти та версію
    archive_url = selected_component['latest_component_data']
    version = selected_component['latest_version']
    try:
        # Створити шлях до папки компоненти згідно назви та версії
        component_folder = os.path.join(project_folder)
        os.makedirs(component_folder, exist_ok=True)
        # Завантажити архів компоненти
        response = requests.get(archive_url, stream=True)
        response.raise_for_status()
        # Шлях до завантаженого архіву
        archive_path = os.path.join(component_folder, f"{selected_component['name']}.zip")
        # Зберегти архів на диск
        with open(archive_path, "wb") as file:
            for chunk in response.iter_content(chunk_size=8192):
                file.write(chunk)
        # Розпакувати архів
        with zipfile.ZipFile(archive_path, "r") as zip_ref:
            zip_ref.extractall(component_folder)
        # Видалити архів
        os.remove(archive_path)
        #перейменувати якщо git
        component_name = selected_component['name']
        old_folder_path = os.path.join(component_folder, component_name + ".git")
        new_folder_path = os.path.join(component_folder, component_name)
        # Перевірка наявності папки зі старою назвою
        if os.path.exists(old_folder_path) and os.path.isdir(old_folder_path):
            # Перейменування папки зі старою назвою на нову назву
            os.rename(old_folder_path, new_folder_path)

        # Шлях до файлу, до якого потрібно додати  роути
        file_path = 'routes.py'
        # Код, який потрібно додати
        if component_name == 'k2grid_base':
            code = f'''from components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/')'''
        elif component_name == 'k2site':
            code = f'''login_manager = k2().init_lm(app)\nfrom components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/{selected_component['name']}')'''
        else:
            code = f'''from components.{selected_component['name']}.{selected_component['name']}.views import {selected_component['name']}\napp.register_blueprint({selected_component['name']}, url_prefix='/{selected_component['name']}')'''



        # Відкриття файлу у режимі дозапису
        with open(file_path, 'a') as file:
            # Запис нового коду у файл
            file.write('\n')
            file.write(code)
            file.write('\n')

        # Update database with the installed component
        component = Component.query.filter_by(name=selected_component['name']).first()
        if not component:
            component = Component(
                name=selected_component['name'],
                description=selected_component['description'],
                version=version,
                git_link=selected_component['git_link'],
                dependencies=code,
                installed=True
            )
            db.session.add(component)
        else:
            component.version = version
            component.git_link = selected_component['git_link']
            component.dependencies = code
            component.installed = True

        db.session.commit()

        # add language folders
        k2.search_babel_translation_directories()
        return f'''Component installed successfully: {selected_component["name"]} v{version} {k2.babel_translation_directories},
                \n \n please wait installing requirments...     
                <meta http-equiv="refresh" content="0;url=/install-requirments/{selected_component["name"]}" />'''

    except Exception as e:
        return f'Error installing component: {str(e)}'


@components_bp.route('/install-requirments/<string:selected_component_name>', methods=['GET'])
def install_requirments(selected_component_name):

    try:
        if k2.platform == 'Windows':
            # install requirements for windows
            venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/Scripts')
            requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
            if os.path.isfile(requirements_file):
                pip_command = f"{venv_bin_path}/python -m pip install -r {requirements_file}"
                subprocess.run(pip_command, shell=True, check=True)
        elif k2.platform == 'Linux':
            #install requirements for linux
            venv_bin_path = os.path.join(os.path.dirname(sys.prefix), 'venv/bin')
            requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
            if os.path.isfile(requirements_file):
                pip_command = f"{venv_bin_path}/python3 -m pip install -r {requirements_file}"
                subprocess.run(pip_command, shell=True, check=True)

        return f'''Requirements installed successfully
         \n \n please wait installing requirments components...     
         <meta http-equiv="refresh" content="1;url=/install-requirments-components" />'''
    except Exception as e:
        return f'Error installing requirmets: {str(e)}'

@components_bp.route('/install-requirments-components')
def install_requirements_components():
    # Откриття файлу requirements_components.txt
    requirements_file = 'requirements_components.txt'
    component_ids = None
    if os.path.isfile(requirements_file):
        with open('requirements_components.txt', 'r') as file:
            component_ids = file.read().splitlines()
    if component_ids:
        for component_id in component_ids:
            # Виклик роуту '/install_components/<string:component_name>' для кожної назви компоненти
            response = requests.get(
                f'/install_components/{component_id}')
        return f'Requirements  components  installed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    else:
        return f'<meta http-equiv="refresh" content="1;url=/dashboard" />'
        # Опрацювання відповіді (за потреби)


@components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
def remove_dependencies(component_id):
    # Знаходимо компоненту за її ID
    component = Component.query.get(component_id)
    if not component:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'
    # remove menu items
    component_name = component.name
    # file_path = "components/menu.yml"
    # remove_from_yaml_file(file_path, component_name)
    requests.get(f"{k2.domain}api/add-to-menu")
    # remove routes
    file_path = 'routes.py'
    with open(file_path, 'r') as file:
        lines = file.readlines()

    updated_lines = [line for line in lines if line.strip() not in component.dependencies]
    with open(file_path, 'w') as file:
        file.writelines(updated_lines)

    component.installed = False
    db.session.commit()
    
    return 'Component successfully turn off <meta http-equiv="refresh" content="1;url=/dashboard" />'


@components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
def add_dependencies(component_id):
    # Знаходимо компоненту за її ID
    component = Component.query.get(component_id)
    if not component:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    file_path = 'routes.py'
    code = component.dependencies
    # Відкриття файлу у режимі дозапису
    with open(file_path, 'a') as file:
        # Запис нового коду у файл
        file.write(code)

    # Оновлюємо статус компоненти
    component.installed = True
    db.session.commit()
    
    return 'Dependencies added successfully  <meta http-equiv="refresh" content="1;url=/dashboard" />'


@components_bp.route('/remove-component/<string:component_id>')
def remove_component(component_id):
    # Шлях до головної папки проекту
    project_folder = 'components'
    component = Component.query.get(component_id)
    # Назва репозиторія
    repository_name = component.name
    # Шлях до папки репозиторія в межах проекту
    repository_folder = os.path.join(project_folder, repository_name)
    try:
        # Перевірка наявності папки репозиторія
        if os.path.exists(repository_folder):
            # Видалення папки репозиторія
            shutil.rmtree(repository_folder)
        # Видаляємо компоненту з бази
        if component:
            db.session.delete(component)
            db.session.commit()

        # Повертаємо повідомлення про успішне видалення
        return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'
    except Exception as e:
        # Повертаємо повідомлення про помилку видалення
        return 'Error removing component: ' + str(e)
    


# @components_bp.route('/component/add')
# def component_add():
#     # components for install
#     try:
#         # GET-requests to API
#         response = requests.get(f'{k2.update_domain}api/components')
#         json_data = response.json()
#         for item in json_data:
#             item['button'] = f"{k2.domain}/install_components/{item['id']}"
#
#     except:
#         json_data = None
#     return jsonify(json_data)
#
#
# @components_bp.route('/component/list')
# def component_list():
#     # Встановлені компоненти
#     component_list = []
#     components = Component.query.all()
#     for component in components:
#         component_dict = {}
#         component_dict['name'] = component.name
#         component_dict['id'] = component.id
#         component_dict['description'] = component.description
#         component_dict['version'] = component.version
#         component_dict['button_off'] =f"{k2.domain}/remove_dependencies/{component.id}"
#         component_dict['button_on'] = f"{k2.domain}/add_dependencies/{component.id}"
#         component_dict['button_del'] = f"{k2.domain}/remove-component/{component.id}"
#         component_list.append(component_dict)
#     return jsonify(component_list)


# menu
@components_bp.route('/api/add-to-menu', methods=['GET'])
def add_to_menu():
    data = []
    response = requests.get(f"{k2.domain}/menu-admin-items")
    data.append(response.json()[0])
    prev_menu = response.json()[0]['title']
    for item in response.json()[0]['children']:
        name_menu = 'k2itm'+ item['to'].replace('/','-')
        caption_menu = item['title']
        add_menu_with_permissions_db(name_menu, prev_menu, caption_menu)
    components = Component.query.all()
    components_names = [component.name for component in components]
    for components_names in components_names:
        response = requests.get(f"{k2.domain}{components_names}/menu-admin-items")
        if response.status_code == 200:
            data.append(response.json()[0])
            #prev_menu =
            prev_menu = response.json()[0]['title']
            for item in response.json()[0]['children']:
                name_menu = 'k2itm'+ item['to'].replace('/','-')
                caption_menu = item['title']
                add_menu_with_permissions_db(name_menu, prev_menu, caption_menu)
        else:
            continue
        # add component_name key for menu items
        #for item in data:
        #    item['component_name'] = components_names
    #k2.menu = data
    return data




@components_bp.route('/api/main-menu/')
@login_required
def get_admin_menu():
    components = Component.query.all()
    components_names = [component.name for component in components]
    if 'k2site' in components_names:
        role_id = current_user.get_role_id()
    filter_menu = []
    response = requests.get(f"{k2.domain}api/add-to-menu")
    menu_items_list = K2admin_menus.get_menu_items_by_roles(role_id)
    for item in response.json():
        filtered_children = []
        for im in item['children']:
            if 'k2itm' + im['to'].replace('/','-') in menu_items_list:
                filtered_children.append(im)
        if filtered_children:
            item['children'] = filtered_children
            filter_menu.append(item)
            # filter_menu.append({"heading": 'APPS & PAGES'})
    return jsonify(filter_menu)


def add_menu_with_permissions_db(name_menu, prev_menu, caption_menu):

    # Створення об'єкта k2admin_menus
    menu = text('SELECT COUNT(*) FROM k2admin_menus WHERE namemenu = :name_menu')
    result = db.session.execute(menu, {'name_menu': name_menu}).fetchone()
    count = result[0]
    if count == 0:
        new_menu_element = K2admin_menus(
            namemenu=name_menu,
            prevmenu=prev_menu,
            caption=caption_menu,
            module_name=name_menu
        )
        db.session.add(new_menu_element)
        db.session.commit()
        # Створення об'єкта k2admin_menus_prava
        new_prava = K2admin_Menus_Prava(
            menuid=new_menu_element.menuid,  # Зв'язуємо зовнішній ключ з menuid нового меню
            username=None,
            r=0,
            w=0,
            i=0,
            d=0,
            c=0,
            exp=0,
            imp=0,
            settable=0,
            cutpast=0,
            enable=0,
            roleid=-1
        )
        # Додавання об'єкта k2admin_menus_prava до сесії
        db.session.add(new_prava)
        db.session.commit()

@components_bp.route('/component-add')
@login_required
def component_add_dasb():
    try:
        response = requests.get(f'{k2.update_domain}api/components')
        json_data = response.json()
        # Перетворення JSON-об'єкту на масив
        component_server = [item for item in json_data]
    except:
        component_server = None
    # Отримання значення пошукового запиту з параметрів URL
    search_query = request.args.get('search_query')
    # print(search_query)
    filtered_components = []
    if search_query:
        filtered_components = [component for component in component_server if
                               (search_query.lower() in component['name'].lower() if component[
                                   'name'] else False) or
                               (search_query.lower() in component['description'].lower() if component[
                                   'description'] else False)]
    else:
        filtered_components = component_server
    components = Component.query.all()
    components_names = [component.name for component in components]
    components_version = [{component.name: component.version} for component in components]

    return render_template('dashboard-add.html',
                           component_server=filtered_components, search_query=search_query, components_names=components_names, components_version=components_version )



@components_bp.route('/component-list')
@login_required
def component_list_dasb():
    components = Component.query.all()

    return render_template('dashboard-list.html', components=components)




@components_bp.route('/menu-admin-items')
def menu_items():
    menu = [
        {
            "title": 'Add components',
            "icon": {"icon": 'mdi-account-circle-outline'},
            "children": [
                {'title': 'Install new components', 'to': '/component-add'},
                {'title': 'Installed components', 'to': '/component-list'},

            ],
        }]
    return menu







