"""Creates fake Spark DataFrames to be used on test cases.

This module is mainly used by a conftest file for creating fixtures using fake
Spark DataFrames. The idea behind this script file is to provide data to be
used to test features from other library modules.

___
"""

# Importing libraries
from pyspark.sql import SparkSession
from pyspark.sql.types import StructType, StructField

from faker import Faker
from decimal import Decimal
from random import randrange


# Creating a faker object
faker = Faker()
Faker.seed(42)


def fake_dataframe(spark_session: SparkSession, dtypes_list: list,
                   num_rows: int = 5):
    """Creates a Spark DataFrame with fake data using Faker.

    This function receives a list of Spark data types imported from
    pyspark.sql.types to create a schema with column names based on the
    data type name (typeName()) and fake data using faker providers.

    Examples:
        ```python
        # Importing classes
        from pyspark.sql import SparkSession
        from pyspark.sql.types import StructType, StructField, StringType,\
            IntegerType, DecimalType, DateType, TimestampType, BooleanType

        from tests.helper.faker import fake_dataframe

        # Creating a list with all types to be used on faking a Spark DataFrame
        dtypes_list = [
            StringType, IntegerType, DecimalType, DateType, TimestampType,
            BooleanType
        ]

        # Calling function to create a fake Spark DataFrame
        df_fake = fake_dataframe(spark, dtypes_list)
        ```

    Args:
        spark_session (SparkSession):
            A SparkSession object for calling the createDataFrame method.

        dtypes_list (list):
            A list with Spark data types to be used as schema for the
            fake DataFrame to be created.

        num_rows (int):
            Total number of rows of the fake DataFrame.

    Returns:
        A new Spark DataFrame with fake data generated by Faker providers
    """

    # Creating a schema with fields based on data types provided on the list
    fake_schema = StructType([
        StructField(f"{dtype.typeName()}_field", dtype(), nullable=True)
        for dtype in dtypes_list
    ])

    # Creating fake data based on each schema attribute
    fake_data_list = []
    for _ in range(num_rows):
        # Iterting over columns and faking data
        fake_row = []
        for field in fake_schema:
            dtype = field.dataType.typeName()
            if dtype == "string":
                fake_row.append(faker.word())
            elif dtype == "integer":
                fake_row.append(randrange(1, 100000))
            elif dtype == "decimal":
                fake_row.append(Decimal(randrange(1, 100000)))
            elif dtype == "boolean":
                fake_row.append(faker.boolean())
            elif dtype == "date":
                fake_row.append(faker.date_this_year())
            elif dtype == "timestamp":
                fake_row.append(faker.date_time_this_year())

        # Appending the row to the data list
        fake_data_list.append(fake_row)

    # Generating a list of tuples
    fake_data = [tuple(row) for row in fake_data_list]

    # Returning a fake Spark DataFrame
    return spark_session.createDataFrame(data=fake_data, schema=fake_schema)
