import json
from datetime import datetime

import humps
import pytest

from django_scaffolding_tools.builders import build_serializer_data, build_serializer_template_data
from django_scaffolding_tools.parsers import (
    parse_dict,
    parse_file_for_ast_classes,
    parse_file_for_enum,
    parse_for_patterns,
    parse_var_name,
    transform_dict_to_model_list,
)
from django_scaffolding_tools.patterns import PATTERN_FUNCTIONS
from django_scaffolding_tools.utils.core import quick_write
from django_scaffolding_tools.writers import ReportWriter


class TestParseDataDictionary:
    def test_parse(self, d_local_data_dict):
        parsed_dict = parse_dict(d_local_data_dict, model_name="Payment")

        assert parsed_dict["payment"]["level"] == 0
        assert parsed_dict["payment"]["attributes"][0]["name"] == "id"
        assert parsed_dict["payment"]["attributes"][0]["value"] == "D-4-be8eda8c-5fe7-49dd-8058-4ddaac00611b"
        assert parsed_dict["payment"]["attributes"][0]["supported"]
        assert parsed_dict["payment"]["attributes"][0]["native"]
        assert not parsed_dict["payment"]["attributes"][0]["many"]
        assert parsed_dict["payment"]["attributes"][0]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][0]["length"] == 40
        assert parsed_dict["payment"]["attributes"][1]["name"] == "amount"
        assert parsed_dict["payment"]["attributes"][1]["value"] == 72.0
        assert parsed_dict["payment"]["attributes"][1]["supported"]
        assert parsed_dict["payment"]["attributes"][1]["native"]
        assert not parsed_dict["payment"]["attributes"][1]["many"]
        assert parsed_dict["payment"]["attributes"][1]["data_type"] == "float"
        assert parsed_dict["payment"]["attributes"][2]["name"] == "status"
        assert parsed_dict["payment"]["attributes"][2]["value"] == "PAID"
        assert parsed_dict["payment"]["attributes"][2]["supported"]
        assert parsed_dict["payment"]["attributes"][2]["native"]
        assert not parsed_dict["payment"]["attributes"][2]["many"]
        assert parsed_dict["payment"]["attributes"][2]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][2]["length"] == 4
        assert parsed_dict["payment"]["attributes"][3]["name"] == "status_detail"
        assert parsed_dict["payment"]["attributes"][3]["value"] == "The payment was paid."
        assert parsed_dict["payment"]["attributes"][3]["supported"]
        assert parsed_dict["payment"]["attributes"][3]["native"]
        assert not parsed_dict["payment"]["attributes"][3]["many"]
        assert parsed_dict["payment"]["attributes"][3]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][3]["length"] == 21
        assert parsed_dict["payment"]["attributes"][4]["name"] == "status_code"
        assert parsed_dict["payment"]["attributes"][4]["value"] == "200"
        assert parsed_dict["payment"]["attributes"][4]["supported"]
        assert parsed_dict["payment"]["attributes"][4]["native"]
        assert not parsed_dict["payment"]["attributes"][4]["many"]
        assert parsed_dict["payment"]["attributes"][4]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][4]["length"] == 3
        assert parsed_dict["payment"]["attributes"][5]["name"] == "currency"
        assert parsed_dict["payment"]["attributes"][5]["value"] == "USD"
        assert parsed_dict["payment"]["attributes"][5]["supported"]
        assert parsed_dict["payment"]["attributes"][5]["native"]
        assert not parsed_dict["payment"]["attributes"][5]["many"]
        assert parsed_dict["payment"]["attributes"][5]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][5]["length"] == 3
        assert parsed_dict["payment"]["attributes"][6]["name"] == "country"
        assert parsed_dict["payment"]["attributes"][6]["value"] == "AR"
        assert parsed_dict["payment"]["attributes"][6]["supported"]
        assert parsed_dict["payment"]["attributes"][6]["native"]
        assert not parsed_dict["payment"]["attributes"][6]["many"]
        assert parsed_dict["payment"]["attributes"][6]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][6]["length"] == 2
        assert parsed_dict["payment"]["attributes"][7]["name"] == "payment_method_id"
        assert parsed_dict["payment"]["attributes"][7]["value"] == "RP"
        assert parsed_dict["payment"]["attributes"][7]["supported"]
        assert parsed_dict["payment"]["attributes"][7]["native"]
        assert not parsed_dict["payment"]["attributes"][7]["many"]
        assert parsed_dict["payment"]["attributes"][7]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][7]["length"] == 2
        assert parsed_dict["payment"]["attributes"][8]["name"] == "payment_method_type"
        assert parsed_dict["payment"]["attributes"][8]["value"] == "TICKET"
        assert parsed_dict["payment"]["attributes"][8]["supported"]
        assert parsed_dict["payment"]["attributes"][8]["native"]
        assert not parsed_dict["payment"]["attributes"][8]["many"]
        assert parsed_dict["payment"]["attributes"][8]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][8]["length"] == 6
        assert parsed_dict["payment"]["attributes"][9]["name"] == "payment_method_flow"
        assert parsed_dict["payment"]["attributes"][9]["value"] == "REDIRECT"
        assert parsed_dict["payment"]["attributes"][9]["supported"]
        assert parsed_dict["payment"]["attributes"][9]["native"]
        assert not parsed_dict["payment"]["attributes"][9]["many"]
        assert parsed_dict["payment"]["attributes"][9]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][9]["length"] == 8
        assert parsed_dict["payment"]["attributes"][10]["name"] == "payer"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["name"] == "Payer"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["level"] == 1
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["name"] == "name"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["value"] == "Nino Deicas"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["many"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][0]["length"] == 11
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["name"] == "user_reference"
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["value"] == "US-jmh3gb4kj5h34"
        )
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["many"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][1]["length"] == 16
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["name"] == "email"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["value"] == "buyer@gmail.com"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["many"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][2]["length"] == 15
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["name"] == "address"
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"]["name"]
            == "Address"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"]["level"]
            == 2
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][0]["name"]
            == "street"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][0]["value"]
            == "123th street"
        )
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][0]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][0]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][0]["many"]
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][0]["data_type"]
            == "str"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][0]["length"]
            == 12
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][1]["name"]
            == "state"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][1]["value"]
            == "FL"
        )
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][1]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][1]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][1]["many"]
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][1]["data_type"]
            == "str"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][1]["length"]
            == 2
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][2]["name"]
            == "zip_code"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][2]["value"]
            == "99999999"
        )
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][2]["supported"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][2]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
            "attributes"
        ][2]["many"]
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][2]["data_type"]
            == "str"
        )
        assert (
            parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["value"]["address"][
                "attributes"
            ][2]["length"]
            == 8
        )
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["supported"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["many"]
        assert parsed_dict["payment"]["attributes"][10]["value"]["payer"]["attributes"][3]["data_type"] == "Address"
        assert parsed_dict["payment"]["attributes"][10]["supported"]
        assert not parsed_dict["payment"]["attributes"][10]["native"]
        assert not parsed_dict["payment"]["attributes"][10]["many"]
        assert parsed_dict["payment"]["attributes"][10]["data_type"] == "Payer"
        assert parsed_dict["payment"]["attributes"][11]["name"] == "order_id"
        assert parsed_dict["payment"]["attributes"][11]["value"] == "4m1OdghPUQtg"
        assert parsed_dict["payment"]["attributes"][11]["supported"]
        assert parsed_dict["payment"]["attributes"][11]["native"]
        assert not parsed_dict["payment"]["attributes"][11]["many"]
        assert parsed_dict["payment"]["attributes"][11]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][11]["length"] == 12
        assert parsed_dict["payment"]["attributes"][12]["name"] == "notification_url"
        assert parsed_dict["payment"]["attributes"][12]["value"] == "http://www.merchant.com/notifications"
        assert parsed_dict["payment"]["attributes"][12]["supported"]
        assert parsed_dict["payment"]["attributes"][12]["native"]
        assert not parsed_dict["payment"]["attributes"][12]["many"]
        assert parsed_dict["payment"]["attributes"][12]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][12]["length"] == 37
        assert parsed_dict["payment"]["attributes"][13]["name"] == "created_date"
        assert parsed_dict["payment"]["attributes"][13]["value"] == "2019-06-26T15:17:31.000+0000"
        assert parsed_dict["payment"]["attributes"][13]["supported"]
        assert parsed_dict["payment"]["attributes"][13]["native"]
        assert not parsed_dict["payment"]["attributes"][13]["many"]
        assert parsed_dict["payment"]["attributes"][13]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][13]["length"] == 28
        assert parsed_dict["payment"]["attributes"][14]["name"] == "user"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["name"] == "User"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["level"] == 1
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["name"] == "id"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["value"] == 1
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["supported"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["native"]
        assert not parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["many"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][0]["data_type"] == "int"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["name"] == "username"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["value"] == "bwayne"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["supported"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["native"]
        assert not parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["many"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["data_type"] == "str"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][1]["length"] == 6
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["name"] == "created"
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["value"] == datetime(
            2022, 10, 21, 11, 56, 25, 23
        )
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["supported"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["native"]
        assert not parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["many"]
        assert parsed_dict["payment"]["attributes"][14]["value"]["user"]["attributes"][2]["data_type"] == "datetime"
        assert parsed_dict["payment"]["attributes"][14]["supported"]
        assert not parsed_dict["payment"]["attributes"][14]["native"]
        assert not parsed_dict["payment"]["attributes"][14]["many"]
        assert parsed_dict["payment"]["attributes"][14]["data_type"] == "User"


def test_simple_parsing(output_folder):
    data = {
        "id": "D-4-be8eda8c-5fe7-49dd-8058-4ddaac00611b",
        "amount": 72.00,
        "status": "PAID",
        "status_detail": "The payment was paid.",
        "status_code": "200",
        "currency": "USD",
        "country": "AR",
        "payment_method_id": "RP",
        "payment_method_type": "TICKET",
        "payment_method_flow": "REDIRECT",
        "payer": {
            "name": "Nino Deicas",
            "user_reference": "US-jmh3gb4kj5h34",
            "email": "buyer@gmail.com",
            "address": {"street": "123th street", "state": "FL", "zip_code": "99999999"},
        },
        "order_id": "4m1OdghPUQtg",
        "notification_url": "http://www.merchant.com/notifications",
        "created_date": "2019-06-26T15:17:31.000+0000",
        "user": {"id": 1, "username": "bwayne", "created": datetime.now()},
    }
    # 1. Parse raw dictionary
    parsed_dict = parse_dict(data)
    quick_write(parsed_dict, "parsed.json")
    # 2. Transform dictionary to a list of models
    model_list = transform_dict_to_model_list(parsed_dict)
    model_list = parse_for_patterns(model_list, PATTERN_FUNCTIONS)
    model_list = build_serializer_data(model_list)

    quick_write(model_list, "model_list.json")
    template_model_list = build_serializer_template_data(model_list)
    quick_write(template_model_list, "template_model_list.json")

    writer = ReportWriter()
    output_file = output_folder / "serializers.py"
    writer.write("drf_serializers.py.j2", output_file, template_data=template_model_list)
    assert output_file.exists()


def test_simple_parsing_camel_case(output_folder, camel_case_dict):
    prefix = "camel_case"
    parsed_dict = parse_dict(camel_case_dict)
    quick_write(parsed_dict, f"{prefix}_parsed.json")

    model_list = transform_dict_to_model_list(parsed_dict)
    model_list = parse_for_patterns(model_list, PATTERN_FUNCTIONS)
    kmodel_list = build_serializer_data(model_list)

    quick_write(model_list, f"{prefix}_model_list.json")

    template_model_list = build_serializer_template_data(model_list)
    quick_write(template_model_list, f"{prefix}_serializer_template_data.json")

    writer = ReportWriter()
    output_file = output_folder / f"{prefix}_serializers_camel_case.py"
    writer.write("drf_serializers.py.j2", output_file, template_data=template_model_list)
    assert output_file.exists()


def test_attributes_with_list(output_folder, fixtures_folder):
    filename = fixtures_folder / "api_bulk.json"
    with open(filename) as json_file:
        raw_data_dict = json.load(json_file)
    prefix = "_with_list"

    parsed_dict = parse_dict(raw_data_dict)
    quick_write(parsed_dict, f"{prefix}_parsed.json")

    model_list = transform_dict_to_model_list(parsed_dict)
    model_list = parse_for_patterns(model_list, PATTERN_FUNCTIONS)
    model_list = build_serializer_data(model_list)

    quick_write(model_list, f"{prefix}_model_list.json")

    template_model_list = build_serializer_template_data(model_list)
    quick_write(template_model_list, f"{prefix}_serializer_template_data.json")

    writer = ReportWriter()
    output_file = output_folder / f"{prefix}_serializers_camel_case.py"
    writer.write("drf_serializers.py.j2", output_file, template_data=template_model_list)
    assert output_file.exists()


def test_parsefile_for_ast_classes(fixtures_folder, output_folder):
    module_file = "pydantic_models.py"
    filename = fixtures_folder / module_file

    ast_module = parse_file_for_ast_classes(filename)
    quick_write(ast_module, f"{module_file}.json")


class TestParseVarName:
    def test_parse_camel_case(self):
        varname = "mySpecialVariable"
        snake_case_varname, was_changed = parse_var_name(varname)
        assert snake_case_varname == "my_special_variable"
        assert was_changed

    def test_parse_pascal_case(self):
        varname = "PaymentSerializer"
        snake_case_varname, was_changed = parse_var_name(varname)
        assert snake_case_varname == "payment_serializer"
        assert was_changed

    def test_parse_snake_case(self):
        varname = "flow_id"
        snake_case_varname, was_changed = parse_var_name(varname)
        assert snake_case_varname == "flow_id"
        assert not was_changed

    @pytest.mark.parametrize("varname, expected", [("IMEIValue", "imei_value"), ("DGINumberId", "dgi_number_id")])
    def test_parse_with_acronym(self, varname, expected):
        snake_case_varname, was_changed = parse_var_name(varname)
        assert snake_case_varname == expected
        assert was_changed


def test_to_camel_case():
    data = {
        "amount": 100,
        "payer": {"fullName": "Nichole Stevens", "email": "ljones@example.org", "document": "07323861808"},
        "merchant_id": "something",
        "phone_number": "56985845",
    }
    dict_camelized = humps.camelize(data)
    # quick_write(dict_camelized, 'camelized_dict.json')
    assert dict_camelized["merchantId"] == data["merchant_id"]
    assert dict_camelized["phoneNumber"] == data["phone_number"]
    assert dict_camelized["payer"]["fullName"] == data["payer"]["fullName"]


def test_parse_file_for_enum(fixtures_folder):
    csv_file = fixtures_folder / "enum_str.csv"
    results = parse_file_for_enum(csv_file)
