"""

    AUTHOR:

        Khushal P Soonderji

    DATE:

        CREATED: Thu, 2nd Apr, 2026

    OBJECTIVE:

        To fix team information issues.

        Cosec has some identification points for each team member. These are noted in TCAOFF in 'json_notes.cosec'. This
        script is meant to go through the team members on TCAOFF, see whose cosec information is either missing or
        malformed, and recreate it.

    REFERENCES:

        N/A

    DOWNLOADS:

        N/A

"""


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                     IMPORT                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")

# For system-level activities:
import os
import copy
from pathlib import Path

# To work with date and time:
import time
import datetime

# To work with tabulate data:
import pandas as pd

# Cosec-related:
from cosec_web.cosec_web import CosecWeb

# TCAOFF-related:
from tcaoff.async_tcaoff import AsyncTheCAOffice

# My utils:
from utils_v2.system import files
from utils_v2.string import json
from utils_v2.string import regex
from utils_v2.string import fuzzy
from utils_v2.date_time import date_time

# To work with datatypes:
from typing import List, Dict, Any
from collections import defaultdict

# To run a cron-like scheduler:
from apscheduler.schedulers.asyncio import AsyncIOScheduler

# For async activities:
import asyncio

# For debugging:
from icecream import IceCreamDebugger

# Common and Shared:
from scripts import common


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                             MACROS / ONE-TIME INIT                                            ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# Debugging:
printer = IceCreamDebugger(prefix = "Manual Team | ", includeContext = True)
err_printer = IceCreamDebugger(prefix = "[ERR] Manual Team | ", includeContext = True)


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   VARIABLES                                                   ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                    CLASSES                                                    ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


# --- Nothing Yet


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                   FUNCTIONS                                                   ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


def enlist_teams_to_fix(
        tcaoff_team_list: list,
        cosec_team_list: list,
) -> list:

    """
    Gives you a list of TCAOFF team members whose Cosec details are missing or malformed.
    This does NOT enlist missing names. The focus of this function is only fixing.
    :param tcaoff_team_list: The list of team members from TCAOFF's '/team/list' API.
    :param cosec_team_list: The list of team members from Cosec's Muster Roll report.
    :return: The list of TCAOFF team members with read-to-use correct data.
    """

    # Prepare the variables:
    teams_to_fix = []
    cosec_id_to_cosec_team_map = {ct["User ID"]: ct for ct in cosec_team_list}

    for tt in tcaoff_team_list:

        # Extract Cosec notes:
        json_notes = json.from_string(tt.get("json_notes", "{}"))
        applicant_notes = json_notes.get("applicantNotes") or {}
        cosec_notes = applicant_notes.get("cosec") or {}

        # Now check if any fix is required.
        # Fixes will be required when:
        #   1. The team member is present in TCAOFF,
        #   2. the team member either has no cosec notes, or the notes are malformed.
        user_id = cosec_notes.get("User ID", cosec_notes.get("UserID"))
        if not user_id:
            if (valid_cose_notes := cosec_id_to_cosec_team_map.get(tt["pseudonym"])) is not None:
                applicant_notes["cosec"] = valid_cose_notes
                tt.update({"applicantNotes": applicant_notes})
                teams_to_fix.append(tt)

    # Done here:
    return teams_to_fix


# ---------------------------------------------------------------------------------------------------------------------


# def enlist_missing_teams(
#         tcaoff_team_list: list,
#         cosec_team_list: list
# ) -> list:
#
#     pass


# ---------------------------------------------------------------------------------------------------------------------


async def manual_team_sync(
        cosec_creds: dict,
        tcaoff_client: AsyncTheCAOffice,
        on_date: date_time.datetime = None,
        test_mode: bool = False,
) -> None:

    """
    To manually sync. teams between Cosec (source) and TCAOFF (dest).
    :param cosec_creds: The credentials to log into Cosec.
    :param tcaoff_client: The credentials to log into TCAOFF.
    :param on_date: The date for which the teams need to be synchronized. That day's Cosec Muster Roll will be fetched
        and those records will be sync'd.
    :param test_mode: Enable this during development or local testing.
    :return: None.
    """

    printer("MANUAL TEAM SYNC")

    # Try the whole process once:
    try:

        # Log in to TCAOFF:
        success = await tcaoff_client.login()
        if not success: raise RuntimeError("TCAOFF Login Failed!")

        # Try the part that needs COSEC:
        try:

            # Figure out the date:
            now = date_time.get_current_ist_date_time()
            if not on_date: on_date = now

            # Get the previous day's In/Out Summary and then wait
            # for the driver's resources to get freed:
            success = common.get_muster_roll(
                cosec_creds = cosec_creds,
                on_date = on_date,
                cache_file = common.get_manual_muster_roll_cache_file_path,
                test_mode = test_mode
            )
            success = True

            # Sync data between Cosec and TCAOFF:
            if success:
                # --- Data Fetch:
                printer("MUSTER ROLL: Sync'ing with TCAOFF")
                cosec_teams = json.from_file(common.get_manual_muster_roll_cache_file_path())["report"]
                tcaoff_teams = await tcaoff_client.team_list()
                # --- Team Fix:
                teams_to_fix = enlist_teams_to_fix(tcaoff_teams, cosec_teams)
                for t in teams_to_fix:
                    printer("Fixing", t["user_id"], t["full_name"])
                    await tcaoff_client.team_update(
                        user_id = t["user_id"],
                        dept_id = t["department_id"],
                        team_name = t["full_name"],
                        email = t["email"],
                        phone_no = t["phone_number"],
                        role = t["role"],
                        applicant_notes = t["applicantNotes"],
                    )


        # If something goes wrong in the COSEC step:
        except Exception as exception:
            err_printer(exception)
            if test_mode: raise

        # Log out of TCAOFF:
        success = await tcaoff_client.logout()
        if not success: raise RuntimeError("TCAOFF Logout Failed!")

    # If something goes wrong:
    except Exception as exception:
        err_printer(exception)
        await tcaoff_client.logout()
        if test_mode: raise


#   *****************************************************************************************************************
#  *****                                                                                                          ****
# ***                                                  MAIN PROGRAM                                                 ***
#  *****                                                                                                          ****
#   *****************************************************************************************************************


if __name__ == "__main__":

    # Parse args:
    import argparse
    ap = argparse.ArgumentParser()
    ap.add_argument(
        "--test", "--test-mode",
        action = "store_true",
        default = False
    )
    ap.add_argument(
        "--verbose",
        action = "store_true",
        default = False
    )
    ap.add_argument(
        "--from-date",
        help = "The date from which you want to sync attendance.",
        default = date_time.get_current_ist_date_time()
    )
    ap.add_argument(
        "--on-date",
        help = "The date for which you want to sync. teams.",
        default = date_time.get_current_ist_date_time()
    )
    ap.add_argument(
        "--cosec-creds",
        help = "The credentials JSON from which you would like to connect to COSEC.",
        default = "cosec.json"
    )
    ap.add_argument(
        "--tcaoff-creds",
        help = "The credentials JSON from which you would like to connect to TheCAOffice.",
        default = "tcaoff.json"
    )
    ap.add_argument(
        "--org", "--organization",
        help = "When handling multiple organizations, this word will be used to keep their files separate.",
        default = "default"
    )
    args = ap.parse_args()

    # Hold temporary environment variables:
    os.environ["ORG"] = args.org

    # Read required credentials:
    cosec_creds_path = os.path.join(common.CREDS_DIR, args.cosec_creds)
    tcaoff_creds_path = os.path.join(common.CREDS_DIR, args.tcaoff_creds)
    # ---
    cosec_creds = json.from_file(cosec_creds_path)
    tcaoff_creds = json.from_file(tcaoff_creds_path)

    # Explicitly mention the expected file paths for other devs to maintain:
    print("PROJ. DIR.  :", common.PROJ_DIR)
    print("COSEC CREDS :", cosec_creds_path)
    print("TCAOFF CREDS:", tcaoff_creds_path)

    # Ensure that certain required paths exist:
    common.init_paths()

    # Date-handling:
    args.on_date = date_time.parse_date_time(args.on_date, timezone = date_time.TIMEZONE_IST)
    args.on_date = args.on_date.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
    print("On Date:", args.on_date)

    # Create the clients:
    tcaoff_client = AsyncTheCAOffice(
        username = tcaoff_creds["creds"]["username"],
        password = tcaoff_creds["creds"]["password"],
        debug = True,
        debug_only_errors = False if args.verbose else True,
    )

    # Schedule the activities:
    asyncio.run(
        manual_team_sync(
            cosec_creds = cosec_creds,
            tcaoff_client = tcaoff_client,
            on_date = args.on_date,
            test_mode = args.test,
        )
    )
