import zipfile
from flask import Flask, Blueprint, jsonify, render_template, redirect, url_for
import os
import shutil
from flask_login import current_user
from flask_bcrypt import bcrypt
from flask_login import login_user, current_user,  logout_user, login_required
from datetime import datetime
from flask_login import UserMixin
import uuid
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length
from flask import request
from db.database import db
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_migrate import Migrate
from flask import current_app as k2
from main import app
import requests
import subprocess
from flask import current_app

import string

with app.app_context():
    bcrypt = Bcrypt(app)
    login_manager = LoginManager(app)
    login_manager.login_view = 'login'

    #migrate = Migrate(app, 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)])

    class LoginForm(FlaskForm):
        username = StringField('Username', validators=[DataRequired(), Length(min=3, max=20)])
        password = PasswordField('Password', validators=[DataRequired()])
        submit = SubmitField('Login')

    class User(UserMixin, db.Model):
        __bind_key__ = 'db2'
        __tablename__ = 'user'
        __table_args__ = {'extend_existing': True}
        id = db.Column(db.String(36), primary_key=True, default=generate_id)
        username = db.Column(db.String(50))
        active = db.Column(db.Boolean)
        last_name = db.Column(db.String(50))
        first_name = db.Column(db.String(50))
        middle_name = db.Column(db.String(50))
        email = db.Column(db.String(100))
        phone = db.Column(db.String(20))
        rights = db.Column(db.String(50))
        manufacturer = db.Column(db.String(50))
        manufacturer_id = db.Column(db.Integer)
        created_date = db.Column(db.DateTime)
        created_by = db.Column(db.String(50))
        oblast_id = db.Column(db.Integer)
        password = db.Column(db.String(50))
        #role_id = db.Column(db.Integer, db.ForeignKey('role.id', name='fk_user_role_id'))
        #role = db.relationship('main.Role', backref='users')
        def __repr__(self):
            return f'<User {self.username}>'

    class Role(db.Model):
        __bind_key__ = 'db2'
        __tablename__ = 'role'
        __table_args__ = {'extend_existing': True}
        id = db.Column(db.String(36), primary_key=True, default=generate_id)
        name = db.Column(db.String(50))
        sorting = db.Column(db.Integer)
        note = db.Column(db.String(100))
        enabled = db.Column(db.Boolean)
        public = db.Column(db.Boolean)
        is_admin = db.Column(db.Boolean)
        actions = db.Column(db.String(100))
        def __repr__(self):
            return f'<Role {self.name}>'
        def __repr__(self):
            return f'<User {self.username}>'

    class Component(db.Model):
        __bind_key__ = 'db2'
        __tablename__ = 'component'
        __table_args__ = {'extend_existing': True}
        id = db.Column(db.String(36), primary_key=True, default=generate_id)
        name = db.Column(db.String(100), nullable=False)
        description = db.Column(db.String(200))
        version = db.Column(db.String(20))
        git_link = db.Column(db.String(200))
        dependencies = db.Column(db.String)
        date_installed = db.Column(db.DateTime, default=datetime.utcnow)
        installed = db.Column(db.Boolean, default=False)

        def __init__(self, name, description, version, git_link,  dependencies, installed):
            self.name = name
            self.description = description
            self.version = version
            self.git_link = git_link
            self.dependencies = dependencies
            self.installed = installed
        def __repr__(self):
            return f"Component('{self.name}', '{self.version}')"
        @classmethod
        def get_repository_by_id(self,rep_id):
            component = db.session.query(Component).filter(Component.id == rep_id).first()
            return component

    components_bp = Blueprint('components', __name__, template_folder='templates')
    login_manager = LoginManager(k2)
    @login_manager.user_loader
    def load_user(user_id):
        # Код для завантаження користувача з бази даних за його ідентифікатором
        return User.query.get(user_id)
    @components_bp.route('/', methods=['GET', 'POST'])
    def home():

        if current_user.is_authenticated:
            # Логіка для авторизованого користувача
            return redirect('/dashboard')
        else:
            # Логіка для неавторизованого користувача
            return redirect('/login')
    @components_bp.route('/login', methods=['GET', 'POST'])
    def login():

        form = LoginForm()
        if form.validate_on_submit():
            # Отримання даних з форми
            username = form.username.data
            password = form.password.data
            # Перевірка введених даних
            user = User.query.filter_by(username=username).first()
            if user and bcrypt.check_password_hash(user.password, password):   #
                login_user(user)
                return redirect('/dashboard')
            else:
                # Невірні дані для авторизації
                error_message = f'Неправильне ім`я користувача чи пароль {username} {password} {user.password}'

                return render_template('/login.html', form=form, error_message=error_message)

        return render_template('/login.html', form=form)
    @components_bp.route('/logout')
    @login_required
    def logout():
        logout_user()
        return redirect('/login')
    @components_bp.route('/dashboard')
    def dashboard():
        if not current_user.is_authenticated:
            return redirect('/login')
            # Компоненти доступні для встановлення
        try:
            # GET-запит до API
            response = requests.get('http://127.0.0.1:8001/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]

        if current_user.is_authenticated:
            username = current_user.username
            users_data = User.query.all()
        else:
            username = ''


        # Отримання значення пошукового запиту з параметрів 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

        return render_template('dashboard.html', username=username, users=users_data, components=components,
                               components_names=components_names, component_server=filtered_components,
                               search_query=search_query)

    @components_bp.route('/show_components/<string:component_id>')
    @login_required
    def show_components(component_id):
        response = requests.get('http://127.0.0.1:8001/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('/install_components_bl/<string:component_id>')
    @login_required
    def install_components_bl(component_id):
        # Шлях до головної папки проекту
        project_folder = 'components'
        #component = Component.get_repository_by_id(component_id)
        response = requests.get('http://127.0.0.1:8001/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>')
    @login_required
    def install_component_from_archive(component_id):
        # Шлях до головної папки проекту
        project_folder = 'components'
        # Отримати відповідну компоненту зі списку компонент
        response = requests.get('http://127.0.0.1:8001/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")
            # Встановити залежності з файлу requirements.txt

            # Зберегти архів на диск
            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)

            # Шлях до файлу, до якого потрібно додати код
            file_path = 'routes.py'
            # Код, який потрібно додати
            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']}')'''
            #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:
                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()

            return f'Component installed successfully: {selected_component["name"]} v{version}, 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'])
    @login_required
    def install_requirments(selected_component_name):
        requirements_file = os.path.join('components', selected_component_name, "requirements.txt")
        if os.path.isfile(requirements_file):
            pip_command = f"{current_app.config['VENV_BIN_PATH']}/python -m pip install -r {requirements_file}"
            subprocess.run(pip_command, shell=True, check=True)
        return f'Requirements installed successfully: <meta http-equiv="refresh" content="1;url=/dashboard" />'


    @components_bp.route('/remove_dependencies/<string:component_id>', methods=['GET'])
    @login_required
    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" />'

        # Шлях до файлу, з якого потрібно видалити залежності
        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 'Dependencies removed successfully <meta http-equiv="refresh" content="1;url=/dashboard" />'

    @components_bp.route('/add_dependencies/<string:component_id>', methods=['GET'])
    @login_required
    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>')
    @login_required
    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)
    app.register_blueprint(components_bp)