from django.core.management.base import BaseCommand
from Subscriptions.models import (
    RegistrationForm,
    RegistrationFileGroup
)
from Auth.models import Profile, ProfileRole
from Common.models import (
    PaymentModeType,
    PaymentPlanType,
    Level
)
from School.models import (
    FeeType,
    Speciality,
    Teacher,
    DiscountType,
    Fee,
    Discount,
)
from django.utils import timezone
from dateutil.relativedelta import relativedelta
import os
from django.conf import settings
from faker import Faker
import random
import json

from School.serializers import (
    FeeSerializer,
    DiscountSerializer,
    PaymentModeSerializer,
    PaymentPlanSerializer,
    SpecialitySerializer,
    TeacherSerializer,
    SchoolClassSerializer
)
from Auth.serializers import ProfileSerializer, ProfileRoleSerializer
from Establishment.serializers import EstablishmentSerializer
from Subscriptions.serializers import StudentSerializer

from Subscriptions.util import getCurrentSchoolYear, getNextSchoolYear  # Import des fonctions nécessaires

# Définir le chemin vers le dossier mock_datas
MOCK_DATAS_PATH = os.path.join(settings.BASE_DIR, 'School', 'management', 'mock_datas')

class Command(BaseCommand):
    help = 'Initialise toutes les données mock'

    def handle(self, *args, **kwargs):
        self.init_establishments()
        self.init_profiles()
        self.init_fees()
        self.init_discounts()
        self.init_payment_modes()
        self.init_payment_plans()
        self.init_specialities()
        self.init_teachers()
        self.init_school_classes()
        self.init_file_group()
        self.init_register_form()

    def load_data(self, filename):
        with open(os.path.join(MOCK_DATAS_PATH, filename), 'r') as file:
            return json.load(file)

    def init_establishments(self):
        establishments_data = self.load_data('establishments.json')

        self.establishments = []
        for establishment_data in establishments_data:
            serializer = EstablishmentSerializer(data=establishment_data)
            if serializer.is_valid():
                establishment = serializer.save()
                self.establishments.append(establishment)
                self.stdout.write(self.style.SUCCESS(f'Establishment {establishment.name} created or updated successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for establishment: {serializer.errors}'))

    def init_profiles(self):
        fake = Faker()

        for _ in range(50):
            # Générer des données fictives pour le profil
            profile_data = {
                "username": fake.user_name(),
                "email": fake.email(),
                "password": "Provisoire01!",
                "code": "",
                "datePeremption": ""
            }

            # Créer le profil
            profile_serializer = ProfileSerializer(data=profile_data)
            if profile_serializer.is_valid():
                profile = profile_serializer.save()
                profile.set_password(profile_data["password"])
                profile.save()
                self.stdout.write(self.style.SUCCESS(f'Profile {profile.email} created successfully'))

                # Créer entre 1 et 3 ProfileRole pour chaque profil
                num_roles = random.randint(1, 3)
                created_roles = set()
                for _ in range(num_roles):
                    establishment = random.choice(self.establishments)
                    role_type = random.choice([ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN, ProfileRole.RoleType.PROFIL_PARENT])

                    # Vérifier si le rôle existe déjà pour cet établissement
                    if (establishment.id, role_type) in created_roles:
                        continue

                    profile_role_data = {
                        "profile": profile.id,
                        "establishment": establishment.id,
                        "role_type": role_type,
                        "is_active": random.choice([True, False])
                    }

                    profile_role_serializer = ProfileRoleSerializer(data=profile_role_data)
                    if profile_role_serializer.is_valid():
                        profile_role_serializer.save()
                        created_roles.add((establishment.id, role_type))
                        self.stdout.write(self.style.SUCCESS(f'ProfileRole for {profile.email} created successfully with role type {role_type}'))
                    else:
                        self.stdout.write(self.style.ERROR(f'Error in data for profile role: {profile_role_serializer.errors}'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for profile: {profile_serializer.errors}'))

    def init_fees(self):
        fees_data = self.load_data('fees.json')

        for fee_data in fees_data:
            establishment = random.choice(self.establishments)
            print(f'establishment : {establishment}')
            fee_data["name"] = fee_data['name']
            fee_data["establishment"] = establishment.id
            fee_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])

            serializer = FeeSerializer(data=fee_data)
            if serializer.is_valid():
                fee = serializer.save()
                self.stdout.write(self.style.SUCCESS(f'Fee {fee.name} created successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for fee: {serializer.errors}'))

    def init_discounts(self):
        discounts_data = self.load_data('discounts.json')

        for discount_data in discounts_data:
            establishment = random.choice(self.establishments)
            discount_data["name"] = discount_data['name']
            discount_data["establishment"] = establishment.id
            discount_data["type"] = random.choice([FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE])
            discount_data["discount_type"] = random.choice([DiscountType.CURRENCY, DiscountType.PERCENT])

            serializer = DiscountSerializer(data=discount_data)
            if serializer.is_valid():
                discount = serializer.save()
                self.stdout.write(self.style.SUCCESS(f'Discount {discount.name} created successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for discount: {serializer.errors}'))

    def init_payment_modes(self):
        modes = list(PaymentModeType.objects.filter(code__in=["SEPA", "TRANSFER", "CHECK", "CASH"]))
        types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]

        for establishment in self.establishments:
            for mode in modes:
                for type in types:
                    payment_mode_data = {
                        "mode": mode.pk,
                        "type": type,
                        "establishment": establishment.id
                    }

                    serializer = PaymentModeSerializer(data=payment_mode_data)
                    if serializer.is_valid():
                        payment_mode = serializer.save()
                        self.stdout.write(self.style.SUCCESS(f'Payment Mode {payment_mode} created successfully'))
                    else:
                        self.stdout.write(self.style.ERROR(f'Error in data for payment mode: {serializer.errors}'))

    def init_payment_plans(self):
        frequencies = list(PaymentPlanType.objects.filter(code__in=["ONE_TIME", "THREE_TIMES", "TEN_TIMES", "TWELVE_TIMES"]))
        types = [FeeType.REGISTRATION_FEE, FeeType.TUITION_FEE]
        current_date = timezone.now().date()

        for establishment in self.establishments:
            for frequency in frequencies:
                for type in types:
                    payment_plan_data = {
                        "frequency": frequency.pk,
                        "type": type,
                        "establishment": establishment.id,
                        "due_dates": self.generate_due_dates(frequency, current_date)
                    }

                    serializer = PaymentPlanSerializer(data=payment_plan_data)
                    if serializer.is_valid():
                        payment_plan = serializer.save()
                        self.stdout.write(self.style.SUCCESS(f'Payment Plan {payment_plan} created successfully'))
                    else:
                        self.stdout.write(self.style.ERROR(f'Error in data for payment plan: {serializer.errors}'))

    def generate_due_dates(self, frequency, start_date):
        if frequency.code == "ONE_TIME":
            return [start_date + relativedelta(months=1)]
        elif frequency.code == "THREE_TIMES":
            return [start_date + relativedelta(months=1+4*i) for i in range(3)]
        elif frequency.code == "TEN_TIMES":
            return [start_date + relativedelta(months=1+i) for i in range(10)]
        elif frequency.code == "TWELVE_TIMES":
            return [start_date + relativedelta(months=1+i) for i in range(12)]

    def init_specialities(self):
        specialities_data = self.load_data('specialities.json')

        for speciality_data in specialities_data:
            establishment = random.choice(self.establishments)
            speciality_data["name"] = speciality_data['name']
            speciality_data["establishment"] = establishment.id

            serializer = SpecialitySerializer(data=speciality_data)
            if serializer.is_valid():
                speciality = serializer.save()
                self.stdout.write(self.style.SUCCESS(f'Speciality {speciality.name} created successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for speciality: {serializer.errors}'))

    def init_teachers(self):
        fake = Faker()

        # Récupérer tous les profils existants avec un rôle ECOLE ou ADMIN
        profiles_with_roles = Profile.objects.filter(roles__role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN]).distinct()

        if not profiles_with_roles.exists():
            self.stdout.write(self.style.ERROR('No profiles with role_type ECOLE or ADMIN found'))
            return

        used_profiles = set()

        for _ in range(15):
            # Récupérer un profil aléatoire qui n'a pas encore été utilisé
            available_profiles = profiles_with_roles.exclude(id__in=used_profiles)
            if not available_profiles.exists():
                self.stdout.write(self.style.ERROR('Not enough profiles with role_type ECOLE or ADMIN available'))
                break

            profile = random.choice(available_profiles)
            used_profiles.add(profile.id)

            # Récupérer les ProfileRole associés au profil avec les rôles ECOLE ou ADMIN
            profile_roles = ProfileRole.objects.filter(profile=profile, role_type__in=[ProfileRole.RoleType.PROFIL_ECOLE, ProfileRole.RoleType.PROFIL_ADMIN])

            if not profile_roles.exists():
                self.stdout.write(self.style.ERROR(f'No ProfileRole with role_type ECOLE or ADMIN found for profile {profile.email}'))
                continue

            profile_role = random.choice(profile_roles)

            # Générer des données fictives pour l'enseignant
            teacher_data = {
                "last_name": fake.last_name(),
                "first_name": fake.first_name(),
                "profile_role": profile_role.id
            }

            establishment_specialities = list(Speciality.objects.filter(establishment=profile_role.establishment))
            num_specialities = min(random.randint(1, 3), len(establishment_specialities))
            selected_specialities = random.sample(establishment_specialities, num_specialities)

            # Créer l'enseignant si il n'existe pas
            teacher_serializer = TeacherSerializer(data=teacher_data)
            if teacher_serializer.is_valid():
                teacher = teacher_serializer.save()
                # Associer les spécialités
                teacher.specialities.set(selected_specialities)
                teacher.save()
                self.stdout.write(self.style.SUCCESS(f'Teacher {teacher.last_name} created successfully for establishment {profile_role.establishment.name}'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for teacher: {teacher_serializer.errors}'))

        self.stdout.write(self.style.SUCCESS('Teachers initialized or updated successfully'))

    def init_school_classes(self):
        school_classes_data = self.load_data('school_classes.json')
        levels = list(Level.objects.all())

        for index, class_data in enumerate(school_classes_data, start=1):
            # Randomize establishment
            establishment = random.choice(self.establishments)
            class_data["atmosphere_name"] = f"Classe {index}"
            class_data["establishment"] = establishment.id

            # Randomize levels
            class_data["levels"] = [level.id for level in random.sample(levels, random.randint(1, min(5, len(levels))))]

            # Randomize teachers
            establishment_teachers = list(Teacher.objects.filter(profile_role__establishment=establishment))
            if len(establishment_teachers) > 0:
                num_teachers = min(2, len(establishment_teachers))
                selected_teachers = random.sample(establishment_teachers, num_teachers)
                teachers_ids = [teacher.id for teacher in selected_teachers]
            else:
                teachers_ids = []

            # Use the serializer to create or update the school class
            class_data["teachers"] = teachers_ids
            serializer = SchoolClassSerializer(data=class_data)
            if serializer.is_valid():
                school_class = serializer.save()
                self.stdout.write(self.style.SUCCESS(f'SchoolClass {school_class.atmosphere_name} created or updated successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for school class: {serializer.errors}'))

        self.stdout.write(self.style.SUCCESS('SchoolClasses initialized or updated successfully'))

    def init_file_group(self):
        fake = Faker()

        for establishment in self.establishments:
            for i in range(1, 4):  # Créer 3 groupes de fichiers par établissement
                name = f"Fichiers d'inscription - {fake.word()}"
                description = fake.sentence()
                group_data = {
                    "name": name,
                    "description": description,
                    "establishment": establishment
                }
                RegistrationFileGroup.objects.update_or_create(name=name, defaults=group_data)
                self.stdout.write(self.style.SUCCESS(f'RegistrationFileGroup {name} initialized or updated successfully'))

        self.stdout.write(self.style.SUCCESS('All RegistrationFileGroups initialized or updated successfully'))

    def init_register_form(self):
        fake = Faker('fr_FR')  # Utiliser le locale français pour Faker

        file_group_count = RegistrationFileGroup.objects.count()
        levels = list(Level.objects.all())

        # Récupérer tous les profils existants avec un ProfileRole Parent
        profiles_with_parent_role = Profile.objects.filter(roles__role_type=ProfileRole.RoleType.PROFIL_PARENT).distinct()

        if not profiles_with_parent_role.exists():
            self.stdout.write(self.style.ERROR('No profiles with ProfileRole Parent found'))
            return

        used_profiles = set()

        for _ in range(50):
            # Récupérer un profil aléatoire qui n'a pas encore été utilisé
            available_profiles = profiles_with_parent_role.exclude(id__in=used_profiles)
            if not available_profiles.exists():
                self.stdout.write(self.style.ERROR('Not enough profiles with ProfileRole Parent available'))
                break

            profile = random.choice(available_profiles)
            used_profiles.add(profile.id)

            # Récupérer le ProfileRole Parent associé au profil
            profile_roles = ProfileRole.objects.filter(profile=profile, role_type=ProfileRole.RoleType.PROFIL_PARENT)
            profile_role = random.choice(profile_roles)

            # Générer des données fictives pour le guardian
            guardian_data = {
                "profile_role": profile_role.id,
                "last_name": fake.last_name(),
                "first_name": fake.first_name(),
                "birth_date": fake.date_of_birth().strftime('%Y-%m-%d'),
                "address": fake.address(),
                "phone": "+33122334455",
                "profession": fake.job()
            }

            # Générer des données fictives pour l'étudiant
            student_data = {
                "last_name": fake.last_name(),
                "first_name": fake.first_name(),
                "address": fake.address(),
                "birth_date": fake.date_of_birth(),
                "birth_place": fake.city(),
                "birth_postal_code": fake.postcode(),
                "nationality": fake.country(),
                "attending_physician": fake.name(),
                "level": random.choice(levels).id,
                "guardians": [guardian_data],
                "sibling": []
            }

            # Créer ou mettre à jour l'étudiant
            student_serializer = StudentSerializer(data=student_data)
            if student_serializer.is_valid():
                student = student_serializer.save()
                self.stdout.write(self.style.SUCCESS(f'Student {student.last_name} created successfully'))
            else:
                self.stdout.write(self.style.ERROR(f'Error in data for student: {student_serializer.errors}'))
                continue

            # Récupérer les frais et les réductions
            fees = Fee.objects.filter(id__in=[1, 2, 3, 4])
            discounts = Discount.objects.filter(id__in=[1])

            # Déterminer l'année scolaire (soit l'année en cours, soit l'année prochaine)
            school_year = random.choice([getCurrentSchoolYear(), getNextSchoolYear()])

            # Créer les données du formulaire d'inscription
            register_form_data = {
                "fileGroup": RegistrationFileGroup.objects.get(id=fake.random_int(min=1, max=file_group_count)),
                "establishment": profile_role.establishment,
                "status": fake.random_int(min=1, max=3),
                "school_year": school_year  # Ajouter l'année scolaire
            }

            # Créer ou mettre à jour le formulaire d'inscription
            register_form, created = RegistrationForm.objects.get_or_create(
                student=student,
                establishment=profile_role.establishment,
                defaults=register_form_data
            )

            if created:
                register_form.fees.set(fees)
                register_form.discounts.set(discounts)
                self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} created successfully with school year {school_year}'))
            else:
                self.stdout.write(self.style.SUCCESS(f'RegistrationForm for student {student.last_name} already exists'))

        self.stdout.write(self.style.SUCCESS('50 RegistrationForms initialized or updated successfully'))