from datetime import datetime, timezone
import datetime
import json
import os
import random
from urllib.parse import unquote

import firebase_admin
import pyrebase
from environment import Environment
from exceptions import OfferNotFoundForUserException
from firebase_admin import auth, firestore
from services.dynamic_reward_service import DynamicRewardService
from services.integrations.integrations_service import IntegrationsService
from services.notifications.sms_service import SMSService

from services.scheduling.scheduling_service import SchedulingService


class FirestoreService:

    cred = Environment.FIREBASE_CREDENTIALS

    firebase_admin.initialize_app(cred)
    pyrebase = pyrebase.initialize_app(Environment.FIREBASE_CONFIG)
    auth = pyrebase.auth()

    firestore_client = firestore.client()

    def get_user_file(phone_number):
        return FirestoreService.firestore_client.collection("users").document(phone_number).get()

    def get_autopilot_file(client_id):
        try:
            return FirestoreService.firestore_client.collection("autopilot").document(client_id).get()
        except:
            return {}

    def get_offer_info(offer_id):
        doc_ref = FirestoreService.firestore_client.collection(
            "offers").document(offer_id)
        doc_snapshot = doc_ref.get()
        return doc_snapshot.to_dict()

    def get_client_info(client_id):
        doc_ref = FirestoreService.firestore_client.collection(
            "clients").document(client_id)
        doc_snapshot = doc_ref.get()
        return doc_snapshot.to_dict()

    # Adds an autopilot offer to a user's acceptedOffers and hits them with a text
    def send_offer_to_wallet(phone_number, offer_id, multiplier=1.0):
        user_file = FirestoreService.get_user_file(phone_number)

        if not user_file.exists:
            raise Exception("User file not found.")

        user_file = user_file.to_dict()
        accepted_offers = list(user_file["acceptedOffers"])
        offer_info = FirestoreService.get_offer_info(offer_id)

        if (offer_info.get("compensation") is None or offer_info.get("compensation") == 0) and (offer_info.get("minCompensation") and offer_info.get("maxCompensation")):
            # calculate compensation amount using dynamic reward service
            tailored_min_comp, tailored_max_comp = DynamicRewardService.calculate_tailored_compensation_bounds(offer_info["minCompensation"],
                                                                                                               offer_info["maxCompensation"],
                                                                                                               user_file["instagramFollowerCount"],
                                                                                                               multiplier=multiplier,
                                                                                                               ignore_reach=(offer_info["contentType"] == "ugc" or offer_info.get("isUgc")))
            compensation = tailored_max_comp
        else:
            tailored_max_comp = 0
            tailored_min_comp = 0
            compensation = offer_info.get("compensation")

        # add offer to user's acceptedOffers
        accepted_offer = {
            "UTCTimeAccepted": round(datetime.datetime.utcnow().timestamp() * 1000),
            "UTCTimeRedeemed": 0,
            "clientID": str(offer_info["clientID"]),
            "exampleUGC": offer_info["exampleUGC"],
            "discountCode": "",
            "finalValidation": False,
            "iPosted": False,
            "initialValidation": False,
            "isExpired": False,
            "isRedeemed": False,
            "isRejected": False,
            "locationID": "0",
            "numToPost": 1,
            "offerID": str(offer_id),
            "ugcURL": "",
            "autoAdded": True,
            "compensation": compensation,
            # generate random acceptanceID to differentiate between multiple acceptances of the same offer
            "acceptanceID": ''.join(random.choices("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", k=20))
        }

        # if offer is dynamic, add in multiplier. Otherwise, leave it out.
        if offer_info.get("minCompensation") and offer_info.get("maxCompensation"):
            accepted_offer["multiplier"] = multiplier

        accepted_offers.append(accepted_offer)

        # update user file
        FirestoreService.firestore_client.collection("users").document(phone_number).update({
            "acceptedOffers": accepted_offers
        })

        # send text to alert user
        client_info = FirestoreService.get_client_info(offer_info["clientID"])
        SMSService.send_private_offer_alert_text(phone_number, client_info["accountName"],
                                                 client_ig_handle=client_info["instagramHandle"],
                                                 content_type=offer_info["contentType"],
                                                 credit_type=offer_info["percentOrCashOff"],
                                                 compensation=offer_info.get(
                                                     "compensation"),
                                                 min_compensation=tailored_min_comp,
                                                 max_compensation=tailored_max_comp, whitelabel_url=client_info.get("whiteLabelDomain"), hard_cash=offer_info.get("isCash"))

    def push_offer_to_manual_dashboard(
        phone_number,
        offer_id,
        required_tagged_username,
        client_id,
        location_id,
        ig_is_private,
        content_type,
        ugc_url,
        client_name,
        specific_product,
        acceptance_id,
        tiktok_handle
    ):
       # print out all params
        print("----------")
        print(phone_number)
        print(offer_id)
        print(required_tagged_username)
        print(client_id)
        print(location_id)
        print(ig_is_private)
        print(content_type)
        print(ugc_url)
        print(client_name)
        print(specific_product)
        print(tiktok_handle)
        print("----------")

        user_file = (
            FirestoreService.firestore_client.collection(
                "users").document(str(phone_number)).get()
        )
        print(f"User file: {user_file}")
        user_file = user_file.to_dict()
        print(f"User file dict: {user_file}")
        accepted_offers = list(user_file["acceptedOffers"])
        for offer in accepted_offers:
            if offer["offerID"] == str(offer_id):
                offer_data = offer

        if not offer_data:
            print("Offer not found in user file.")
            raise OfferNotFoundForUserException()

        utc_time_accepted = offer_data["UTCTimeAccepted"]
        user_instagram_handle = user_file["instagramHandle"]
        file_name = str(phone_number) + "_" + str(offer_id) + \
            "_" + str(random.randint(0, 9999999))

        data_to_push = {
            "UTCTimeAccepted": utc_time_accepted,
            "clientInstagramHandle": required_tagged_username,
            "finalValidation": False,
            "instagramHandle": user_instagram_handle,
            "instagramIsPrivate": ig_is_private,
            "offerID": str(offer_id),
            "clientID": str(client_id),
            "locationID": str(location_id),
            "phoneNumber": str(phone_number),
            "requirements": [],
            "status": "pending",
            "contentType": content_type,
            "ugcUrl": ugc_url,
            "clientName": client_name,
            "specificProduct": specific_product,
            "tiktokHandle": tiktok_handle
        }

        if acceptance_id:
            data_to_push["acceptanceID"] = acceptance_id

        doc_ref = FirestoreService.firestore_client.collection(
            "manuallyValidatedOffers"
        ).document(file_name)
        doc_ref.set(data_to_push)
        print("Offer pushed to manual dashboard.")

    def increment_number_offers_left(offer_id):
        offer_ref = FirestoreService.firestore_client.collection(
            "offers").document(offer_id)

        offer_data = offer_ref.get().to_dict()

        offer_data["numberOffersLeft"] += 1

        offer_ref.update(offer_data)

    def create_dashboard_admin_auth_and_file(email, password, account_name):
        user = FirestoreService.auth.create_user_with_email_and_password(
            email, password)

        # 2024-04-04 Evince_dev_python
        # Send verification mail to new seat created
        # FirestoreService.auth.send_email_verification(user.get('idToken'))

        # Do not send email asking to verify, just verify here
        access_user = firebase_admin.auth.get_user_by_email(email)

        updated_user = firebase_admin.auth.update_user(access_user.uid, email_verified=True)

        print(f"Email verification for {email} status updated successfully: ", updated_user.email_verified)

        print("Successfully created auth account: " + email)
        user_specs = auth.verify_id_token(user['idToken'])
        user_uid = user_specs['uid']
        FirestoreService.firestore_client.collection("clientDashboardAdmin").document(user_uid).create({
            "email": email,
            "clientIDs": [],
            "userID": user_uid,
            "accountName": account_name
        })
        print("Successfully created firebase file for dashboard account: " + email)

    def add_additional_seat(email, password, full_name, admin_id):
        # create auth account with email/password
        user = FirestoreService.auth.create_user_with_email_and_password(
            email, password)

        # 2024-04-04 Evince_dev_python
        # Send verification mail to new seat created
        FirestoreService.auth.send_email_verification(user.get('idToken'))
        # create firestore doc in additionalSeats collection with adminID, email, and fullName. (Doc ID is email).
        FirestoreService.firestore_client.collection("additionalSeats").document(email).create({
            "adminID": admin_id,
            "email": email,
            "fullName": full_name,
        })
        # add the email to the additionalSeats array in the admin's file
        admin_file = FirestoreService.firestore_client.collection(
            "clientDashboardAdmin").document(admin_id).get().to_dict()
        # create the additionalSeats array if it doesn't exist
        if "additionalSeats" not in admin_file:
            admin_file["additionalSeats"] = []
        admin_file["additionalSeats"].append(email)
        FirestoreService.firestore_client.collection("clientDashboardAdmin").document(admin_id).update({
            "additionalSeats": admin_file["additionalSeats"]
        })
        print("Successfully created additional seat for: " + email)

    # 2024-04-05 Evince_dev_python
    # delete addition seat
    def delete_additional_seat(email):
        # Delete addition seat record from firestore

        # Get user from firebase Authentication
        user = auth.get_user_by_email(email)

        # Get users additional seat details
        additional_seat = FirestoreService.firestore_client.collection(
            "additionalSeats").document(email)

        additional_seat_doc = additional_seat.get()
        admin_id = additional_seat_doc.get(
            "adminID") if additional_seat_doc.exists else None

        # remove addition seat from clients table
        admin_file = FirestoreService.firestore_client.collection(
            "clientDashboardAdmin").document(admin_id)
        admin_file.update({
            "additionalSeats": firestore.ArrayRemove([email])
        })

        # delete addition seat
        additional_seat.delete()

        # delete user
        auth.delete_user(user.uid)

        print("Successfully deleted additional seat for: " + email)

    # add a new file to firestore with the specified file_name and metadata
    def create_content_metadata_file(file_name, metadata):
        FirestoreService.firestore_client.collection(
            "contentMetadata").document(file_name).set(metadata)

    def update_final_verification(
            phone_number, offer_id, is_valid, ugc_url, content_type, score, acceptance_id=None):
        print(f"Updating final verification for {phone_number}")

        # try to decode (if coming from Flutter, it will be encoded)
        try:
            phone_number = unquote(phone_number)
            ugc_url = unquote(ugc_url)
        except Exception as e:
            print(e)

        user_file = (
            FirestoreService.firestore_client.collection(
                "users").document(phone_number).get()
        )
        user_file = user_file.to_dict()
        accepted_offers = list(user_file["acceptedOffers"])
        compensation_amount = 0
        cash_increase = 0
        discount_amount = 0
        client_info = {}
        approved = False

        for offer in accepted_offers:
            # Ensure offer is the correct offer
            if offer["offerID"] == offer_id and (offer.get("acceptanceID") == acceptance_id or acceptance_id == None):
                if offer[
                    "finalValidation"
                ]:  # if offer is already validated, stop searching and break
                    break
                if is_valid:  # check if the the post was verified successfully
                    offer_info = FirestoreService.get_offer_info(offer_id)
                    client_id = offer_info["clientID"]
                    client_info = FirestoreService.get_client_info(client_id)
                    offer[
                        "ugcURL"
                    ] = ugc_url  # .replace("/", "%2F") # add the ugcURL to the offer, Because we encode the URL, we need to make the link valid by replacing the / with %2F

                    offer["finalValidation"] = True
                    # store current number of followers to maintain accurate stats (in case follower count changes in the future)
                    offer["followers"] = user_file["instagramFollowerCount"]
                    approved = True
                    # set score to score if it's not None and it's an int, otherwise default to 80 (should only be used for force-approvals)
                    score = score if score is not None and isinstance(
                        score, int) else 80
                    whitelabel_url = client_info.get("whiteLabelDomain")
                    discount_code = None

                    # calculate compensation amount (can either be static or dynamic)
                    if offer_info["compensation"]:  # static reward
                        compensation_amount = offer_info.get("compensation")
                    # dynamic reward
                    elif (offer_info.get("minCompensation") and offer_info.get("maxCompensation")):
                        compensation_amount = DynamicRewardService.calculate_rounded_dynamic_compensation_amount(offer_info.get("minCompensation"), offer_info.get("maxCompensation"), score, user_file.get(
                            "instagramFollowerCount"), multiplier=offer.get("multiplier"), ignore_reach=(offer_info.get("contentType") == "ugc" or offer_info.get("isUgc")))
                        # set compensation amount in offer (only necessary for dynamic rewards)
                        offer["compensation"] = compensation_amount

                    if offer_info["isCash"]:
                        cash_increase = compensation_amount  # add cash increase to user's cash balance
                        print(
                            f"Cash increase for offer {str(offer_id)} is {str(cash_increase)}"
                        )
                    else:  # is not a cash offer (schedule reminders to use credit)
                        # Generate discount code via integration(if applicable)
                        discount_type = (
                            "cash"
                            if offer_info["percentOrCashOff"] == "Cash"
                            else "percent"
                        )
                        discount_amount = compensation_amount
                        if offer_info.get(
                            "integrations"
                        ):  # check if the offer has an integration
                            discount_code = IntegrationsService.create_single_use_discount_code(
                                discount_amount,
                                discount_type,
                                offer_info["integrations"],
                                client_info["integrations"],
                            )
                            if discount_code:
                                offer["discountCode"] = discount_code
                                print(
                                    f"Discount code {discount_code} generated for offer {offer_id}"
                                )

                        delays_in_seconds = (
                            [10, 30, 60]
                            if Environment.USE_TEST_MODE
                            else [604800, 1209600, 2419200]
                        )  # in production, send reminders after 1 week, 2 weeks, 1 month
                        for delay in delays_in_seconds:
                            SchedulingService.create_task(
                            http_target=f"{Environment.FLASK_SERVER_URL}/send-sms/credit-reminder/",
                            headers={"Authorization": Environment.FLASK_API_KEY, "Content-Type": "application/json"},
                            body=json.dumps({
                                "phone_number": phone_number,
                                "business_name": client_info['accountName'],
                                "compensation": discount_amount,
                                "credit_type": discount_type,
                                "offer_id": offer_id,
                                "whitelabel_url": client_info.get("whiteLabelDomain"),
                            }),
                            http_method="POST",
                            from_now_in_seconds=delay,
                        )
                    try: # alert user, send new offer is autopilot is enabled
                        SMSService.send_post_approved_text(phone_number, whitelabel_url=whitelabel_url, discount_code=discount_code)
                        delay_in_seconds = 15 if Environment.USE_TEST_MODE else random.randint(79200, 93600) # delay between 22 and 26 hours
                        autopilotData = FirestoreService.get_autopilot_file(client_id)
                        hasAutopilotOffers = bool(autopilotData) and len(autopilotData.keys()) > 1
                        if hasAutopilotOffers:
                            SchedulingService.create_task(
                                http_target=f"{Environment.FLASK_SERVER_URL}/autopilot/send-targeted-offer/",
                                headers={
                                    "Authorization": Environment.FLASK_API_KEY, "Content-Type": "application/json"},
                                http_method='POST',
                                body={"phone_number": phone_number, "event": "content_verification", "client_id": client_id,
                                      "prev_offer_id": offer_id, "prev_multiplier": offer.get("multiplier")},
                                from_now_in_seconds=delay_in_seconds)
                    except:
                        print("error autopilot did not have data")
                else:  # story did not meet offer requirements
                    offer["isRejected"] = True
                    FirestoreService.increment_number_offers_left(
                        offer_id
                    )  # undo the decrement of number of offers left that occurred on "I Posted"
                    SMSService.send_post_rejected_text(
                        phone_number, is_ugc_offer=content_type == "ugc")
                FirestoreService.firestore_client.collection("users").document(phone_number).update(
                    {
                        "acceptedOffers": accepted_offers,
                        "cashBalance": user_file["cashBalance"] + cash_increase,
                    }
                )
                break

        return approved

    def get_instagram_bots(self):
        return FirestoreService.firestore_client.collection("instagramBots").get()

    # Methods for measuring stats for clients, locations, and offers

    # Returns detailed list of client stats for a client, including location-specific and offer-specific stats
    def get_detailed_client_stats(client_id):
        from concurrent.futures import ThreadPoolExecutor

        client_doc = (
            FirestoreService.firestore_client.collection(
                "clients").document(client_id).get()
        )

        offer_stats = {}
        location_stats = {}
        customer_group_stats = {}
        locations_quantity = 0
        offers_quantity = 0
        active_offers_quantity = 0

        if client_doc.exists:
            client_doc = client_doc.to_dict()
            locations_quantity = len(client_doc["locations"])

            def fetch_offer_data(acceptedOffer):
                nonlocal offers_quantity, active_offers_quantity  # Declare as nonlocal
                offer_doc = (
                    FirestoreService.firestore_client.collection("offers")
                    .document(str(acceptedOffer))
                    .get()
                )
                if offer_doc.exists:
                    offers_quantity += 1
                    offer_doc = offer_doc.to_dict()
                    if offer_doc["isActive"]:
                        active_offers_quantity += 1

            # Use ThreadPoolExecutor to fetch offer data in parallel
            with ThreadPoolExecutor() as executor:
                executor.map(fetch_offer_data, client_doc["offers"])
        else:
            return {
                "success": False,
                "error": "Client not found.",
            }  # return error if client not found

        client_signups = 0
        client_posts = 0
        client_reach = 0  # note: offer reach only applies to offers that have been posted and verified. location reach includes all users who signed up for the location
        client_story_reach = 0
        client_acceptances = 0
        client_redemptions = 0
        client_customer_list = (
            {}
        )  # track map of phone numbers to list of offer ids completed by customer

        success = False
        client_signups_doc = (
            FirestoreService.firestore_client.collection(
                "clientSignups").document(client_id).get()
        )

        if not client_signups_doc.exists:
            print("No stats document found. Returning basic client stats.")
            return {
                "success": True,
                "client_stats": {
                    "posts": client_posts,
                    "reach": client_reach,
                    "storyReach": client_story_reach,
                    "signups": client_signups,
                    "redemptions": client_redemptions,
                    "locationQty": locations_quantity,
                    "offerQty": offers_quantity,
                    "activeOfferQty": active_offers_quantity,
                    "customerList": client_customer_list,
                    "acceptances": client_acceptances,
                },
                "location_stats": location_stats,
                "offer_stats": offer_stats,
                "customer_group_stats": customer_group_stats,
            }
        client_signups_doc = client_signups_doc.to_dict()

        # remove signups from list that are not registered in firebase auth
        new_signups_for_client = {}
        for phone_number, location_id in client_signups_doc["signups"].items():
            try:
                auth.get_user_by_phone_number(
                    phone_number
                )  # Check if phone number is registered in firebase auth
                print(
                    f"Phone number {phone_number} is registered in firebase auth.")
                new_signups_for_client[phone_number] = location_id
            except auth.UserNotFoundError:
                print(
                    f"Number: {phone_number} is not registered in firebase auth. Excluding from signups list."
                )
        client_signups_doc["signups"] = new_signups_for_client

        client_signups = len(client_signups_doc["signups"])

        def fetch_user_data(phone_number):
            nonlocal client_signups, client_posts, client_reach, client_story_reach, client_acceptances, client_redemptions
            location_id = client_signups_doc["signups"][phone_number]
            if location_id not in location_stats:
                location_stats[location_id] = {
                    "signups": 0,
                    "posts": 0,
                    "reach": 0,  # note: reach is the same as story reach for locations
                    "acceptances": 0,
                    "redemptions": 0,
                }
            location_stats[location_id]["signups"] += 1

            # get doc from users collection
            user_doc = (
                FirestoreService.firestore_client.collection(
                    "users").document(phone_number).get()
            )
            if user_doc.exists:
                user_doc = user_doc.to_dict()
                if (
                    phone_number not in client_customer_list
                ):  # add new customer to client_customer_list if not already there
                    client_customer_list[phone_number] = {
                        "acceptedOffers": [],
                        "postedOffers": [],
                        "redeemedOffers": [],
                        "reach": user_doc["instagramFollowerCount"],
                    }

                client_reach += user_doc[
                    "instagramFollowerCount"
                ]  # add to total reach for client

                for acceptedOffer in user_doc["acceptedOffers"]:
                    # convert client_id to string if it is an int
                    acceptedOffer["clientID"] = str(acceptedOffer["clientID"])
                    # convert location_id to string if it is an int
                    acceptedOffer["locationID"] = str(
                        acceptedOffer["locationID"])
                    if (
                        acceptedOffer["clientID"] == client_id
                    ):  # check if the offer is for this client
                        if (
                            acceptedOffer["offerID"] not in offer_stats
                        ):  # add new offer to offer_stats if not already there
                            offer_stats[acceptedOffer["offerID"]] = {
                                "posts": 0,
                                "reach": 0,  # note: reach is the same as story reach for offers
                                "acceptances": 0,
                                "redemptions": 0,
                            }
                        if (
                            acceptedOffer["locationID"] not in location_stats
                        ):  # add new location to location_stats if not already there
                            location_stats[acceptedOffer["locationID"]] = {
                                "signups": 0,
                                "posts": 0,
                                "reach": 0,
                                "acceptances": 0,
                                "redemptions": 0,
                            }
                        # increment acceptances for client, location, and offer
                        client_acceptances += 1
                        location_stats[acceptedOffer["locationID"]
                                       ]["acceptances"] += 1
                        offer_stats[acceptedOffer["offerID"]
                                    ]["acceptances"] += 1
                        client_customer_list[phone_number]["acceptedOffers"].append(
                            acceptedOffer["offerID"]
                        )  # add offer to customer's acceptedOffers list

                        if acceptedOffer[
                            "finalValidation"
                        ]:  # check if offer was validated (posted and approved)
                            client_posts += 1  # increment client posts
                            # add to total story reach for client, if a post from the customer has not already been counted
                            if (
                                len(client_customer_list[phone_number]
                                    ["postedOffers"])
                                == 0
                            ):
                                client_story_reach += user_doc["instagramFollowerCount"]
                            location_stats[acceptedOffer["locationID"]][
                                "posts"
                            ] += 1  # increment location posts
                            offer_stats[acceptedOffer["offerID"]][
                                "posts"
                            ] += 1  # increment offer posts
                            offer_stats[acceptedOffer["offerID"]]["reach"] += user_doc[
                                "instagramFollowerCount"
                            ]  # add to offer reach
                            client_customer_list[phone_number]["postedOffers"].append(
                                acceptedOffer["offerID"]
                            )  # add offer to customer's postedOffers list

                            if acceptedOffer[
                                "isRedeemed"
                            ]:  # check if offer was redeemed for credit
                                client_redemptions += 1
                                location_stats[acceptedOffer["locationID"]][
                                    "redemptions"
                                ] += 1
                                offer_stats[acceptedOffer["offerID"]][
                                    "redemptions"
                                ] += 1
                                client_customer_list[phone_number][
                                    "redeemedOffers"
                                ].append(
                                    acceptedOffer["offerID"]
                                )  # add offer to customer's redeemedOffers list

                location_stats[location_id]["reach"] += user_doc[
                    "instagramFollowerCount"
                ]  # add to total reach for location
            else:
                print(
                    f"User {phone_number} not found in users collection. Skipping user."
                )

        # Use ThreadPoolExecutor to fetch user data in parallel
        with ThreadPoolExecutor() as executor:
            executor.map(fetch_user_data, client_signups_doc["signups"])

        if (
            "customerGroups" in client_doc and len(
                client_doc["customerGroups"]) > 0
        ):  # compile stats for each customer group (if client has any customer groups)
            for customer_group_id in client_doc["customerGroups"]:
                customer_group_stats[customer_group_id] = {
                    "customerQtySignedUp": 0,
                    "customerQtyTotal": 0,
                    "posts": 0,
                    "reach": 0,
                    "acceptances": 0,
                    "redemptions": 0,
                }
                # fetch customer group doc
                customer_group_doc = (
                    FirestoreService.firestore_client.collection(
                        "customerGroups")
                    .document(customer_group_id)
                    .get()
                )  # fetch customer group doc
                if customer_group_doc.exists:
                    customer_group_doc = customer_group_doc.to_dict()
                    for customer_phone_number in customer_group_doc["users"]:
                        customer_group_stats[customer_group_id][
                            "customerQtyTotal"
                        ] += 1  # add 1 to total customer qty (customer may or may not actually be signed up) - intended to account for manually added phone numbers
                        try:
                            customer_group_stats[customer_group_id]["posts"] += len(
                                client_customer_list[customer_phone_number][
                                    "postedOffers"
                                ]
                            )
                            customer_group_stats[customer_group_id][
                                "acceptances"
                            ] += len(
                                client_customer_list[customer_phone_number][
                                    "acceptedOffers"
                                ]
                            )
                            customer_group_stats[customer_group_id][
                                "redemptions"
                            ] += len(
                                client_customer_list[customer_phone_number][
                                    "redeemedOffers"
                                ]
                            )
                            customer_group_stats[customer_group_id][
                                "reach"
                            ] += client_customer_list[customer_phone_number]["reach"]
                            customer_group_stats[customer_group_id][
                                "customerQtySignedUp"
                            ] += 1
                        except:
                            print(
                                f"Customer {customer_phone_number} not found in customer list. No stats to add for customer."
                            )

        client_stats = {
            "posts": client_posts,
            "reach": client_reach,
            "storyReach": client_story_reach,
            "signups": client_signups,
            "redemptions": client_redemptions,
            "acceptances": client_acceptances,
            "locationQty": locations_quantity,
            "offerQty": offers_quantity,
            "activeOfferQty": active_offers_quantity,
            "customerList": client_customer_list,
        }

        success = True
        return {
            "success": success,
            "client_stats": client_stats,
            "location_stats": location_stats,
            "offer_stats": offer_stats,
            "customer_group_stats": customer_group_stats,
        }

    # update fields for client_id doc in clientSignups collection
    def update_detailed_client_stats(client_id, stats):
        utc_timestamp = str(
            round(datetime.datetime.utcnow().timestamp() * 1000))
        client_doc = (
            FirestoreService.firestore_client.collection(
                "clientStats").document(client_id).get()
        )
        if client_doc.exists:
            client_doc = client_doc.to_dict()
        else:
            client_doc = {}

        # create copy but without customerList
        stats_to_upload = stats["client_stats"].copy()
        del stats_to_upload["customerList"]
        client_doc[utc_timestamp] = stats_to_upload
        FirestoreService.firestore_client.collection("clientStats").document(client_id).set(
            client_doc
        )

        # update client community stats
        client_community_stats_doc = (
            FirestoreService.firestore_client.collection(
                "clientCommunityStats")
            .document(client_id)
            .get()
        )
        if client_community_stats_doc.exists:
            client_community_stats_doc = client_community_stats_doc.to_dict()
        else:
            client_community_stats_doc = {}

        # set client community stats latest customer list, with customer phone numbers as field names
        for customer in stats["client_stats"]["customerList"]:
            client_community_stats_doc[customer] = stats["client_stats"][
                "customerList"
            ][customer]
        FirestoreService.firestore_client.collection("clientCommunityStats").document(
            client_id
        ).set(client_community_stats_doc)

        for location in stats["location_stats"]:
            if location != "" and location != "0":
                location_doc = (
                    FirestoreService.firestore_client.collection(
                        "locationStats")
                    .document(location)
                    .get()
                )
                if location_doc.exists:
                    location_doc = location_doc.to_dict()
                else:
                    location_doc = {}
                location_doc[utc_timestamp] = stats["location_stats"][location]
                FirestoreService.firestore_client.collection("locationStats").document(
                    location
                ).set(location_doc)

        for offer in stats["offer_stats"]:
            offer_doc = (
                FirestoreService.firestore_client.collection(
                    "offerStats").document(offer).get()
            )
            if offer_doc.exists:
                offer_doc = offer_doc.to_dict()
            else:
                offer_doc = {}
            offer_doc[utc_timestamp] = stats["offer_stats"][offer]
            FirestoreService.firestore_client.collection("offerStats").document(offer).set(
                offer_doc
            )

        for customer_group in stats["customer_group_stats"]:
            customer_group_doc = (
                FirestoreService.firestore_client.collection(
                    "customerGroupStats")
                .document(customer_group)
                .get()
            )
            if customer_group_doc.exists:
                customer_group_doc = customer_group_doc.to_dict()
            else:
                customer_group_doc = {}
            customer_group_doc[utc_timestamp] = stats["customer_group_stats"][
                customer_group
            ]
            FirestoreService.firestore_client.collection("customerGroupStats").document(
                customer_group
            ).set(customer_group_doc)

        return stats

    def get_and_update_detailed_admin_account_stats(admin_id, admin_doc=None):
        admin_stats = {
            "clientQty": 0,
            "locationQty": 0,
            "offerQty": 0,
            "activeOfferQty": 0,
            "signups": 0,
            "reach": 0,
            "storyReach": 0,
            "posts": 0,
            "redemptions": 0,
            "acceptances": 0,
        }
        admin_community_stats = {}

        if admin_doc is None:
            admin_doc = (
                firestore.client.collection("clientDashboardAdmin")
                .document(admin_id)
                .get()
            )

        if admin_doc.exists:
            admin_doc = admin_doc.to_dict()
            # run stats update for each client on admin account
            for client_id in admin_doc["clientIDs"]:
                client_stats = FirestoreService.get_detailed_client_stats(
                    client_id)

                if not client_stats["success"]:
                    print(client_stats["error"])
                    continue

                # increment admin stats according to child clients
                admin_stats["clientQty"] += 1
                admin_stats["locationQty"] += client_stats["client_stats"][
                    "locationQty"
                ]
                admin_stats["offerQty"] += client_stats["client_stats"]["offerQty"]
                admin_stats["activeOfferQty"] += client_stats["client_stats"][
                    "activeOfferQty"
                ]
                admin_stats["signups"] += client_stats["client_stats"]["signups"]
                admin_stats["reach"] += client_stats["client_stats"]["reach"]
                admin_stats["storyReach"] += client_stats["client_stats"]["storyReach"]
                admin_stats["posts"] += client_stats["client_stats"]["posts"]
                admin_stats["redemptions"] += client_stats["client_stats"][
                    "redemptions"
                ]
                admin_stats["acceptances"] += client_stats["client_stats"][
                    "acceptances"
                ]

                # add client community stats to admin community stats
                for customer in client_stats["client_stats"]["customerList"]:
                    admin_community_stats[customer] = client_stats["client_stats"][
                        "customerList"
                    ][customer]

                FirestoreService.update_detailed_client_stats(
                    client_id, client_stats
                )  # update latest stats for client

            utcTimestamp = str(
                round(datetime.datetime.utcnow().timestamp() * 1000)
            )  # add timestamp as utc time in milliseconds, rounded to the nearest millisecond

            # update latest stats for admin account
            admin_stats_doc = (
                FirestoreService.firestore_client.collection(
                    "adminStats").document(admin_id).get()
            )

            if admin_stats_doc.exists:
                admin_stats_doc = admin_stats_doc.to_dict()
            else:
                admin_stats_doc = {}

            # set admin community stats latest customer list, with customer phone numbers as field names
            # overwrite adminCommunityStats doc with latest customer list
            FirestoreService.firestore_client.collection("adminCommunityStats").document(
                admin_id
            ).set(admin_community_stats)

            admin_stats_doc[utcTimestamp] = admin_stats
            FirestoreService.firestore_client.collection("adminStats").document(admin_id).set(
                admin_stats_doc
            )
            return admin_stats

    # update stats for all admin accounts, including all child clients, locations, and offers
    def refresh_and_update_all_stats():
        from concurrent.futures import ThreadPoolExecutor

        admin_docs = FirestoreService.firestore_client.collection(
            "clientDashboardAdmin").get()

        def worker(admin_doc):
            FirestoreService.get_and_update_detailed_admin_account_stats(
                admin_doc.id, admin_doc=admin_doc
            )

        # Use ThreadPoolExecutor to run the workers in parallel
        with ThreadPoolExecutor() as executor:
            executor.map(worker, admin_docs)

        return True

    def update_metadata_stats(file_name, metadata):
        print(f"inside update metadata function. file_name: {file_name} metadata: {metadata}")
        FirestoreService.firestore_client.collection("contentMetadata").document(file_name).update(
            metadata
        )

    # 2024-04-18 Evince_dev_python
    # Get offer details
    def get_offer_stats_details(offer_id):
        offer_details_with_stats = {}
        fields = ["discountCollectionID","ecommerceDiscountCode","isPrivate","contentType","offerID","prerequisiteCollectionID","prerequisiteName","minimumFollowerCount",
                  "minPurchaseAmount","isCustomerOffer","discountItemID","isInfluencerOffer","clientID","offerImage","totalCost","minCompensation","customerGroup",
                  "privateOfferPhoneList","exampleUGC","quantity","offerName","locations","numberOffersLeft","integrations","specificProduct","rewardClientNames","percentOrCashOff",
                  "rewardClientIDs","date","isActive","prerequisiteQuantity","numToPost","isCash","influencerPhoneNumbers","typeOfOffer","compensation","discountItem","maxCompensation",]
        offer_details = FirestoreService.firestore_client.collection(
            "offers").document(offer_id).get(fields).to_dict()
        offer_stats = FirestoreService.firestore_client.collection(
            "offerStats").document(offer_id).get().to_dict()
        offer_details_with_stats.update(offer_details)
        offer_details_with_stats['stats'] = {"timestamps" : offer_stats}
        return offer_details_with_stats
