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
import uuid



# 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 generate_id():
    generated_id = uuid.uuid4().hex[:32]
    return ''.join([generated_id[i:i + 4] for i in range(0, len(generated_id), 4)])

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():
    project_folder = 'components'
    # Зчитати вміст файлу YAML
    with open(f"{project_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    languages_paths = []

    # Додаємо головну директорію languages
    languages_paths.append('languages')

    # Знаходимо шляхи до директорій languages для кожного компонента з даних YAML
    if components_data is not None:
        for component in components_data['components']:
            component_languages_directory = f"components/{component['name']}/{component['name']}/languages"
            languages_paths.append(component_languages_directory)

    result = ';'.join(languages_paths)
    return result


@components_bp.route('/', methods=['GET', 'POST'])
def home():

    components_folder = 'components'

    with open(f"{components_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    if components_data is None:
        return redirect('/dashboard')

    k2site_component = next(
        (comp for comp in components_data['components'] if comp['name'] == 'k2site' and comp.get('installed', False)),
        None)
    if k2site_component:
        return redirect(url_for('k2site.main_page'))  #
    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()
    with open(f"components/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)
    if components_data is None or 'components' not in components_data:
        components = []
    else:
        components = components_data['components']
    components_names = []
    components_names = [component['name'] for component in components]
    k2site_component = next(
        (comp for comp in components_data['components'] if comp['name'] == 'k2site' and comp.get('installed', False)),
        None)
    if k2site_component:
        return redirect(url_for('components.component_list_dasb'))
    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')

        # Зчитати вміст файлу YAML
        with open(f"{project_folder}/components.yml", 'r') as file:
            components_data = yaml.safe_load(file)
        if components_data == None:
            components_data = {'components': []}



        # Перевірити унікальність імен компонентів
        existing_component = next((c for c in components_data['components'] if c['name'] == selected_component['name']),
                                  None)
        if existing_component:
            # Оновити існуючий компонент
            existing_component['id'] = generate_id()
            existing_component['version'] = version
            existing_component['git_link'] = selected_component['git_link']
            existing_component['dependencies'] = code
            existing_component['installed'] = True
        else:
            # Додати новий компонент до списку
            components_data['components'].append({
                'id': generate_id(),
                'name': selected_component['name'],
                'description': selected_component['description'],
                'version': version,
                'git_link': selected_component['git_link'],
                'dependencies': code,
                'installed': True
            })

        # Записати оновлений вміст у файл YAML
        with open(f"{project_folder}/components.yml", 'a') as file:
            yaml.dump(components_data, file)

        # 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" />'
    with open(f"components/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)
    if components_data is None:
        return None
    # Шукати компонент за id
    for component in components_data['components']:
        if component['id'] == component_id:
            component

    # 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
    with open(f"components/components.yml", 'w') as file:
        yaml.dump(components_data, file)


    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):
    # Зчитати вміст файлу YAML
    with open(f"components/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    if components_data is None:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    # Знайти компоненту за ID
    component = None
    for comp in components_data['components']:
        if comp['id'] == component_id:
            component = comp
            break

    if component is None:
        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

    # Оновлення вмісту файлу YAML
    with open(f"components/components.yml", 'w') as file:
        yaml.dump(components_data, file)

    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):
    # Шлях до папки з компонентами
    components_folder = 'components'

    # Зчитати вміст файлу YAML
    with open(f"{components_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    if components_data is None:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    # Знайти компоненту за ID
    component = None
    for comp in components_data['components']:
        if comp['id'] == component_id:
            component = comp
            break

    if component is None:
        return 'Component not found <meta http-equiv="refresh" content="1;url=/dashboard" />'

    # Видалити папку репозиторія компоненти
    repository_folder = os.path.join(components_folder, component['name'])
    if os.path.exists(repository_folder):
        shutil.rmtree(repository_folder)

    # Видалити компоненту зі списку
    components_data['components'] = [comp for comp in components_data['components'] if comp['id'] != component_id]

    # Записати оновлений вміст у файл YAML
    with open(f"{components_folder}/components.yml", 'w') as file:
        yaml.dump(components_data, file)

    return 'Component removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'


# menu
@components_bp.route('/api/add-to-menu', methods=['GET'])
def add_to_menu():
    components_folder = 'components'
    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)
    # Отримати дані з файлу YAML кожного компонента
    with open(f"{components_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    if components_data is not None:
        # Отримати імена всіх компонентів
        components_names = [comp['name'] for comp in components_data['components']]

        for component_name in components_names:
            # Виконати запит до відповідного роуту компонента
            response = requests.get(f"{k2.domain}{component_name}/menu-admin-items")

            if response.status_code == 200:
                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)

    return jsonify(data)


@components_bp.route('/api/main-menu/')
@login_required
def get_admin_menu():

    components_folder = 'components'
    #
    with open(f"{components_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    if components_data is None:
        return jsonify([])
    # Отримати список імен компонентів
    components_names = [comp['name'] for comp in components_data['components']]
    if 'k2site' in components_names:
        role_id = current_user.get_role_id()

        # Виконати запит до роуту '/api/add-to-menu'
        response = requests.get(f"{k2.domain}api/add-to-menu")
        menu_items_list = K2admin_menus.get_menu_items_by_roles(role_id)

        filter_menu = []

        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)

        return jsonify(filter_menu)

    return jsonify([])


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()
        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
    project_folder = 'components'
    with open(f"{project_folder}/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)

    components_names = []
    components_version = []
    # Отримати імена та версії компонентів з даних YAML
    if components_data is not None:
        for component in components_data['components']:
            name = component['name']
            version = component['version']
            components_names.append(name)
            components_version.append({name: version})

    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():
    with open(f"components/components.yml", 'r') as file:
        components_data = yaml.safe_load(file)
    if components_data is None or 'components' not in components_data:
        components = []
    else:
        components = components_data['components']

    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







