import os
import subprocess
import sys
import secrets
import string
import platform
import json
import re
import shutil
from .consts import django_project_run_message, django_requirements
from .DockerConfigConsts import django_docker_config, postgres_docker_config, local_yml_docker_config, production_yml_docker_config

class DjangoProjectCLI:
    def __init__(self, name):
        self.project_name = name
        self.base_dir = os.getcwd()
        self.root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        

    def _generate_secret_key(self, length=50):
        alphabet = string.ascii_letters + string.digits + string.punctuation
        return ''.join(secrets.choice(alphabet) for i in range(length))

    def _create_ssl_certificates(self, path: str, host: str):
        ssl_dir = os.path.join(path, 'ssl')
        os.makedirs(ssl_dir, exist_ok=True)

        # Generar certificados SSL usando OpenSSL
        try:
            subprocess.run([
                'openssl', 'req', '-x509', '-nodes', '-days', '365', '-newkey', 'rsa:2048',
                '-keyout', os.path.join(ssl_dir, 'local.key'),
                '-out', os.path.join(ssl_dir, 'local.crt'),
                '-subj', f'/CN={host}'
            ], check=True)
            return True
        except subprocess.CalledProcessError:
            print(
                "Advertencia: No se pudieron crear los certificados SSL. Por favor, instala OpenSSL e inténtalo de "
                "nuevo.")
            return False

    def _modify_docker_compose(self, project_path: str, https_enabled: bool):
        docker_compose_path = os.path.join(project_path, 'local.yml')
        if not os.path.exists(docker_compose_path):
            print(f"Advertencia: No se encontró el archivo {docker_compose_path}. No se realizaron modificaciones.")
            return

        with open(docker_compose_path, 'r') as file:
            docker_compose_content = file.read()

        if https_enabled:
            docker_compose_content = docker_compose_content.replace(
                'ports:\n      - "8000:8000"',
                'ports:\n      - "443:443"\n      - "8000:8000"'
            )
            docker_compose_content = docker_compose_content.replace(
                'command: /start',
                'command: /start\n    volumes:\n      - ./ssl:/etc/ssl/certs'
            )

        with open(docker_compose_path, 'w') as file:
            file.write(docker_compose_content)
    
    def add_installed_apps(self, project_path: str, app_name: str):
        try:
            # Actualizar settings.py
            settings_path = os.path.join(project_path, 'config\\settings', 'base.py')
            app_surname = ''
            if "." in app_name:
                app_surname = app_name.split('.')[1]

            with open(settings_path, 'a') as f:
                if app_surname not in ['utils', 'users']:
                    f.write(f"\nINSTALLED_APPS += ['{app_name}']\n")
        except Exception as e:
            print(f"❌ Error agregando app a INSTALLED_APPS: {str(e)}")
    
    def _create_utils_directory(self, project_path: str):
        try:
            # Get inner project directory (same name as project)
            project_name = os.path.basename(project_path)
            inner_project_path = os.path.join(project_path, project_name)

            
            utils_src_path = os.path.join(self.root_dir, 'utils')
            utils_dst_path = os.path.join(inner_project_path, 'utils')
            
            # Copy utils directory if it exists
            if os.path.exists(utils_src_path):
                shutil.copytree(utils_src_path, utils_dst_path, dirs_exist_ok=True)
                print("✅ Directorio utils copiado")
            else:
                print("❌ El directorio utils no existe en el root del proyecto")
        except Exception as e:
            print(f"❌ Error copiando directorio utils: {str(e)}")

    def create_django_app(self, default_app_path, project_path, app_name, project_slug):
        
        app_path = os.path.join(default_app_path, app_name)
        os.makedirs(app_path, exist_ok=True)
        
        # Copy the prepared users app folder
        src_users_app_path = os.path.join(default_app_path, app_name)
        dst_users_app_path = os.path.join(project_path + f'\\{project_slug}', app_name)
        
        if os.path.exists(dst_users_app_path):
            print(f"Eliminando carpeta existente: {dst_users_app_path}")
            shutil.rmtree(dst_users_app_path)
        shutil.copytree(src_users_app_path, dst_users_app_path)

        self.add_installed_apps(project_path, project_slug + '.' + app_name)
        
            
        self.replace_project_slug_in_directory(project_path+f'\\{project_slug}'+f'\\{app_name}', project_slug)
            

    def _create_docker_structure(self, project_path: str):
        """Creates Docker files structure"""


        
        # Define base paths
        compose_local = os.path.join(project_path, 'compose', 'local')
        compose_prod = os.path.join(project_path, 'compose', 'production')
        
        # Create directories
        directories = [
            os.path.join(compose_local, 'django'),
            os.path.join(compose_local, 'postgres'),
            os.path.join(compose_local, 'docs'),
            os.path.join(compose_prod, 'django'),
            os.path.join(compose_prod, 'postgres'),
        ]
        
        for dir_path in directories:
            os.makedirs(dir_path, exist_ok=True)

        # Docker files content
        docker_files = {
            os.path.join(compose_local, 'django', 'Dockerfile'): django_docker_config,
            os.path.join(compose_local, 'postgres', 'Dockerfile'): postgres_docker_config,
            'local.yml': local_yml_docker_config,
            'production.yml': production_yml_docker_config,
        }

        # Create files
        for file_path, content in docker_files.items():
            full_path = os.path.join(project_path, file_path)
            os.makedirs(os.path.dirname(full_path), exist_ok=True)
            with open(full_path, 'w') as f:
                f.write(content.strip())

        # Create .env files structure
        env_dirs = [
            os.path.join(project_path, '.envs', '.local'),
            os.path.join(project_path, '.envs', '.production')
        ]
        for dir_path in env_dirs:
            os.makedirs(dir_path, exist_ok=True)

        print("✅ Estructura Docker creada exitosamente")


    def create_project(self, path: str, https_enabled: bool, host: str):
        try:
            print("Creando proyecto Django...")
            project_slug = self.project_name.lower().replace(" ", "_")
            project_path = os.path.join(path, project_slug)

            if os.path.exists(project_path):
                print(f"Error: El directorio '{project_slug}' ya existe en la ruta especificada.")
                sys.exit(1)

            print("Instalando cookiecutter...")
            subprocess.run(["pip", "install", "cookiecutter"], check=True)
                    
            print("Ejecutando cookiecutter con configuración JSON...")
            subprocess.run([
                "cookiecutter",
                "https://github.com/cookiecutter/cookiecutter-django",
                "--no-input",
                f"project_name={self.project_name}",
                f"project_slug={project_slug}",
                f"use_docker=y",
                f"use_celery=y",
                f"postgresql_version=16",
                f"custom_user_module=y",
                f"use_https=y",
                f"host={host}"
            ], check=True, cwd=path)

            
            if https_enabled:
                print("Creando certificados SSL...")
                ssl_success = self._create_ssl_certificates(project_path, host)
                if ssl_success:
                    self._modify_docker_compose(project_path, True)
            # Update server configuration with host
            server_config_path = os.path.join(project_path, 'config', 'settings', 'local.py')
            if os.path.exists(server_config_path):
                with open(server_config_path, 'r') as f:
                    content = f.read()
                
                # Replace or add ALLOWED_HOSTS configuration
                if "ALLOWED_HOSTS" in content:
                    content = re.sub(r'ALLOWED_HOSTS\s*=\s*\[.*?\]', f'ALLOWED_HOSTS = ["{host}"]', content)
                else:
                    content += f'\nALLOWED_HOSTS = ["{host}"]\n'
                
                with open(server_config_path, 'w') as f:
                    f.write(content)
            
            file_dir = os.path.dirname(os.path.abspath(__file__))
            parent_dir = os.path.dirname(file_dir)
            default_app_path = os.path.join(parent_dir, 'default_apps')
            print(f'default_app_path: {default_app_path}')
            input("Presiona Enter para continuar...")
            # Agregar el contenido de django_requirements al final de requirements/base.txt
            requirements_dst_path = os.path.join(project_path, 'requirements', 'base.txt')
            with open(requirements_dst_path, 'a') as dst_file:
                    dst_file.write('\n' + django_requirements)
            
            print(default_app_path)
            input("Presiona Enter para continuar...")
            self.comment_allauth_settings(project_path=project_path)
            self.create_django_app(default_app_path, project_path, 'users', project_slug)
            self.create_django_app(default_app_path, project_path, 'utils', project_slug)
            self.add_installed_apps(project_path, 'auditlog')
            is_venezuelan = input("¿La aplicación será en Venezuela? (y/n): ").strip().lower() == 'y'
            if is_venezuelan:
                self.create_django_app(default_app_path, project_path, 'locations', project_slug)
                self.format_initial_data(file_dir, project_path, project_slug)
            
            print(f"\n¡Proyecto Django '{self.project_name}' creado exitosamente!")
            print(f"Ubicación: {project_path}")
            print(django_project_run_message)

            if https_enabled:
                print(f"\nHTTPS ha sido configurado. Accede a tu sitio en https://{host}:8000")
                print("Nota: Necesitarás aceptar el certificado autofirmado en tu navegador")

        except subprocess.CalledProcessError as e:
            print(f"Error al crear el proyecto Django: {e}")
            sys.exit(1)

    def comment_allauth_settings(self, project_path):
        settings_path = os.path.join(project_path, 'config', 'settings', 'base.py')

        lines_to_comment = [
            'ACCOUNT_ALLOW_REGISTRATION = env.bool("DJANGO_ACCOUNT_ALLOW_REGISTRATION", True)',
            'ACCOUNT_AUTHENTICATION_METHOD = "username"',
            'ACCOUNT_EMAIL_REQUIRED = True',
            'ACCOUNT_EMAIL_VERIFICATION = "mandatory"',
            'ACCOUNT_ADAPTER = ',
            'ACCOUNT_FORMS = ',
            'SOCIALACCOUNT_ADAPTER = ',
            'SOCIALACCOUNT_FORMS = ',
            'allauth'            
        ]
        lines_to_skip = [
            '"allauth.account.middleware.AccountMiddleware",',
            '"allauth.account",'
        ]

        if os.path.exists(settings_path):
            with open(settings_path, 'r') as f:
                content = f.readlines()

            with open(settings_path, 'w') as f:
                for line in content:
                    if any(to_comment in line for to_comment in lines_to_comment) and line.strip() not in lines_to_skip:
                        f.write(f"# {line}") 
                    else:
                        f.write(line)

    
    def replace_project_slug_in_directory(self, directory_path, project_slug):
        """
        Reemplaza todas las ocurrencias de "{{project_slug}}" en todos los archivos .py del directorio especificado y sus subdirectorios por el valor de project_slug.

        :param directory_path: Ruta del directorio.
        :param project_slug: String que reemplazará "{{project_slug}}" en los archivos.
        """
        for root, _, files in os.walk(directory_path):
            for file in files:
                if file.endswith('.py'):
                    file_path = os.path.join(root, file)
                    try:
                        # Leer el contenido del archivo
                        with open(file_path, 'r') as f:
                            content = f.read()

                        # Reemplazar "{{project_slug}}" por el valor de project_slug
                        updated_content = content.replace('{{project_slug}}', project_slug)

                        # Escribir el contenido actualizado de nuevo en el archivo
                        with open(file_path, 'w') as f:
                            f.write(updated_content)

                        print(f"Reemplazo completado en el archivo: {file_path}")

                    except FileNotFoundError:
                        print(f"El archivo no se encontró: {file_path}")
                    except Exception as e:
                        print(f"Ocurrió un error al procesar el archivo: {e}")

    def run_migrations(self, project_path: str):
        try:
            # Navegar al directorio del proyecto
            manage_py_path = os.path.join(project_path, 'manage.py')
            
            # Ejecutar makemigrations
            subprocess.run(['python', manage_py_path, 'makemigrations'], check=True)
            
            # Ejecutar migrate
            subprocess.run(['python', manage_py_path, 'migrate'], check=True)
            
            print("✅ Migraciones ejecutadas exitosamente")
        except subprocess.CalledProcessError as e:
            print(f"❌ Error ejecutando migraciones: {str(e)}")
    
    def format_initial_data(self, file_dir, project_path, app_name):
        initial_data_path = os.path.join(file_dir, 'initial_data.json')
        if os.path.exists(initial_data_path):
            print("Cargando datos iniciales en la base de datos...")
            with open(initial_data_path, 'r') as f:
                data = f.read()
            
            # Reemplazar todas las ocurrencias de "app_name" con el nombre de la aplicación
            data = data.replace('app_name', f'{app_name}')

            dst_data_path = os.path.join(project_path, app_name, 'locations', 'migrations', 'initial_data.json')
            
            # Guardar el contenido modificado en el archivo initial_data.json
            with open(dst_data_path, 'w') as f:
                f.write(data)
            
        else:
            print(f"Error: No se encontró el archivo {initial_data_path}")
