import os
import django
from typing import List, Tuple
from django.utils import timezone
from faker import Faker
from factory.django import DjangoModelFactory
from factory import SubFactory, LazyAttribute, Iterator
from django_extensions.management.commands import runscript

# Set up Django environment
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
django.setup()

from accounts.models import User, Student, Parent, DepartmentHead, LEVEL, RELATION_SHIP
from course.models import Program

fake = Faker()

class UserFactory(DjangoModelFactory):
    """
    Factory for creating User instances with optional flags.
    
    Attributes:
        username (str): The generated username.
        first_name (str): The generated first name.
        last_name (str): The generated last name.
        email (str): The generated email.
        date_joined (datetime): The current date and time.
        phone (str): The generated phone number.
        address (str): The generated address.
        is_student (bool): Flag indicating if the user is a student.
        is_lecturer (bool): Flag indicating if the user is a lecturer.
        is_parent (bool): Flag indicating if the user is a parent.
        is_dep_head (bool): Flag indicating if the user is a department head.
    """
    
    class Meta:
        model = User

    username: str = LazyAttribute(lambda x: fake.user_name())
    first_name: str = LazyAttribute(lambda x: fake.first_name())
    last_name: str = LazyAttribute(lambda x: fake.last_name())
    email: str = LazyAttribute(lambda x: fake.email())
    date_joined: timezone.datetime = timezone.now()
    phone: str = LazyAttribute(lambda x: fake.phone_number())
    address: str = LazyAttribute(lambda x: fake.address())
    is_student: bool = False
    is_lecturer: bool = False
    is_parent: bool = False
    is_dep_head: bool = False

    @classmethod
    def _create(cls, model_class: type, *args, **kwargs) -> User:
        """
        Create a User instance with optional flags.
        
        Args:
            model_class (type): The class of the model to create.

        Returns:
            User: The created User instance.
        """
        user: User = super()._create(model_class, *args, **kwargs)

        # Set the appropriate flags based on the user type
        if cls.is_student:
            user.is_student = True
        elif cls.is_parent:
            user.is_parent = True

        user.save()
        return user

class ProgramFactory(DjangoModelFactory):
    """
    Factory for creating Program instances.

    Attributes:
        title (str): The generated program title.
        summary (str): The generated summary.
    """

    class Meta:
        model = Program

    title: str = LazyAttribute(lambda x: fake.sentence(nb_words=3))
    summary: str = LazyAttribute(lambda x: fake.text())

    @classmethod
    def _create(cls, model_class: type, *args, **kwargs) -> Program:
        """
        Create a Program instance using get_or_create to avoid duplicates.
        
        Args:
            model_class (type): The class of the model to create.

        Returns:
            Program: The created Program instance.
        """
        program, created = Program.objects.get_or_create(title=kwargs.get("title"), defaults=kwargs)
        return program

class StudentFactory(DjangoModelFactory):
    """
    Factory for creating Student instances with associated User and Program.
    
    Attributes:
        student (User): The associated User instance.
        level (str): The level of the student.
        program (Program): The associated Program instance.
    """

    class Meta:
        model = Student

    student: User = SubFactory(UserFactory, is_student=True)
    level: str = Iterator([choice[0] for choice in LEVEL])
    program: Program = SubFactory(ProgramFactory)

class ParentFactory(DjangoModelFactory):
    """
    Factory for creating Parent instances with associated User, Student, and Program.
    
    Attributes:
        user (User): The associated User instance.
        student (Student): The associated Student instance.
        first_name (str): The generated first name.
        last_name (str): The generated last name.
        phone (str): The generated phone number.
        email (str): The generated email.
        relation_ship (str): The relationship with the student.
    """

    class Meta:
        model = Parent

    user: User = SubFactory(UserFactory, is_parent=True)
    student: Student = SubFactory(StudentFactory)
    first_name: str = LazyAttribute(lambda x: fake.first_name())
    last_name: str = LazyAttribute(lambda x: fake.last_name())
    phone: str = LazyAttribute(lambda x: fake.phone_number())
    email: str = LazyAttribute(lambda x: fake.email())
    relation_ship: str = Iterator([choice[0] for choice in RELATION_SHIP])


def generate_fake_accounts_data(num_programs: int, num_students: int, num_parents: int) -> None:
    """
    Generate fake data for Programs, Students, Parents, and DepartmentHeads.

    Args:
        num_programs (int): Number of programs to generate.
        num_students (int): Number of students to generate.
        num_parents (int): Number of parents to generate.
    """
    programs: List[Program] = ProgramFactory.create_batch(num_programs)
    students: List[Student] = StudentFactory.create_batch(num_students)
    parents: List[Parent] = ParentFactory.create_batch(num_parents)

    print(f"Created {len(programs)} programs.")
    print(f"Created {len(students)} students.")
    print(f"Created {len(parents)} parents.")


generate_fake_accounts_data(10, 10, 10)

