"""

    AUTHOR:

        Khushal P Soonderji

    DATE:

        CREATED: Wed, 26th Nov, 2025
        UPDATED: Wed, 26th Nov, 2025

    OBJECTIVE:

        To achieve so-and-so-objective...

    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

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

# To work with tabulate data:
import pandas as pd

# To make API calls:
import requests

# Cosec-related:
from cosec_web.cosec_web import CosecWeb

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

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


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


# --- Nothing Yet


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


# --- Nothing Yet


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


# --- Nothing Yet


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


def remove_special_chars(s: str) -> str:

    return regex.replace(
        text = s,
        pattern = r"[^\w\d\- _]",
        substitute_text = "_"
    )


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


def login(
        tcaoff_creds: dict
) -> bool:

    """
    Log in to TCAOFF. The retrieved session token is then updated in the input dict itself.
    :param tcaoff_creds: The set of credentials to use to log in.
    :return: True if logged in, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["login"],
        json = {
            "username": tcaoff_creds["creds"]["username"],
            "password": tcaoff_creds["creds"]["password"],
            "mode": "cosec"
        }
    )

    # If login succeeded:
    if response.status_code in [200]:
        response_json = response.json()
        tcaoff_creds["creds"]["sessionToken"] = response_json["sessionToken"]
        tcaoff_creds["creds"]["user"] = {
            "userId": 1234
        }
        print("TCAOFF: logged in")
        return True

    # If login failed:
    else:
        print("TCAOFF: log-in failed")
        return False


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


def logout(
        tcaoff_creds: dict
) -> bool:

    """
    Log out from TCAOFF. The session token is cleared from the in-mem creds.
    :param tcaoff_creds: The set of credentials to use to log out.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["logout"],
        json = {"username": tcaoff_creds["creds"]["sessionToken"]}
    )

    # Clear the session details from the in-mem creds:
    tcaoff_creds["creds"]["sessionToken"] = None
    tcaoff_creds["creds"]["user"] = None

    # If logout succeeded:
    if response.status_code in [200]:
        print("TCAOFF: logged out")
        return True

    # If login failed:
    else:
        print("TCAOFF: log-out failed")
        return False


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


def branch_list(
        tcaoff_creds: dict
) -> List[Dict[str, Any]] | None:

    """
    List the existing branches.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["branchList"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
    )

    # If the call succeeded:
    if response.status_code in [200]:
        response_json = response.json()
        branches = response_json["data"]["rs0"]
        print("TCAOFF: branches listed")
        return branches

    # If the call failed:
    else:
        print("TCAOFF: branch-list failed")
        return None


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


def branch_add(
        tcaoff_creds: dict,
        branch_name: str
) -> bool:

    """
    Add a new branch.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :param branch_name: The name of the branch to add.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["branchAdd"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {
            "idUser": tcaoff_creds["creds"]["user"]["userId"],
            "branchName": branch_name
        }
    )

    # Debugging:
    if response.status_code not in [200]:
        response_json = response.json()
        print("TCAOFF Branch-Add:", json.to_string(response_json))

    # If the call succeeded:
    if response.status_code in [200]:
        print(f"TCAOFF: branch '{branch_name}' added")
        return True

    # If the call failed:
    else:
        print(f"TCAOFF: branch '{branch_name}' NOT added")
        return False


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


def department_list(
        tcaoff_creds: dict
) -> List[Dict[str, Any]] | None:

    """
    List the existing departments.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["deptList"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
    )

    # If the call succeeded:
    if response.status_code in [200]:
        response_json = response.json()
        branches = response_json["data"]["rs0"]
        print("TCAOFF: depts. listed")
        return branches

    # If the call failed:
    else:
        print("TCAOFF: dept-list failed")
        return None


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


def department_add(
        tcaoff_creds: dict,
        department_name: str
) -> bool:

    """
    Add a new department.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :param department_name: The name of the department to add.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["deptAdd"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {
            "idUser": tcaoff_creds["creds"]["user"]["userId"],
            "departmentName": department_name
        }
    )

    # Debugging:
    if response.status_code not in [200]:
        response_json = response.json()
        print("TCAOFF Dept-Add:", json.to_string(response_json))

    # If the call succeeded:
    if response.status_code in [200]:
        print(f"TCAOFF: dept. '{department_name}' added")
        return True

    # If the call failed:
    else:
        print(f"TCAOFF: dept. '{department_name}' NOT added")
        return False


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


def team_list(
        tcaoff_creds: dict
) -> List[Dict[str, Any]] | None:

    """
    List the existing team members.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :return: True if logged out, else False.
    """

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["teamList"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {"idUser": tcaoff_creds["creds"]["user"]["userId"]}
    )

    # If the call succeeded:
    if response.status_code in [200]:
        response_json = response.json()
        branches = response_json["data"]["rs0"]
        print("TCAOFF: team listed")
        return branches

    # If the call failed:
    else:
        print("TCAOFF: team-list failed")
        return None


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


def team_add(
        tcaoff_creds: dict,
        branch_id: int,
        dept_id: int,
        reporting_to: int | None,
        team_name: str,
        email: str,
        phone_no: str,
        role: str,
        username: str,
        password: str,
        applicant_notes: dict | list | str = None
) -> bool:

    """
    Add a new team member.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :param dept_id: The id of the department that this team member is working in.
    :param branch_id: The id of the branch that this team member is working in.
    :param reporting_to: The id of the senior to whom this team member will report.
    :param team_name: The name of the team member. This is the full display name. Can be the same as others.
    :param email: The email id of the team member.
    :param phone_no: The phone no. of the team member.
    :param role: The role of the team member in the organization.
    :param username: The unique username of the team member. Cannot be the same as anyone else.
    :param password: The password for this team member's login.
    :param applicant_notes: Optional notes about the team member.
    :return: True if successful, else False.
    """

    # # Ensure that JSON notes are converted to string:
    # if isinstance(applicant_notes, (list, dict)):
        # applicant_notes = json.to_string(applicant_notes, no_space = True)

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["teamAdd"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {
            "branchId": branch_id,
            "idDepartment": dept_id,
            "reportingTo": reporting_to,
            "name": team_name,
            "email": email,
            "phoneNo": phone_no,
            "role": role,
            "username": username,
            "password": password,
            "hierarchy": 1,
            "applicantNotes": applicant_notes
        }
    )

    # Debugging:
    if response.status_code not in [200]:
        response_json = response.json()
        print("TCAOFF Team-Add:", json.to_string(response_json))

    # If the call succeeded:
    if response.status_code in [200]:
        print(f"TCAOFF: team '{team_name} ({username})' added")
        return True

    # If the call failed:
    else:
        print(f"TCAOFF: team '{team_name} ({username})' NOT added")
        return False


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


def team_update(
        tcaoff_creds: dict,
        user_id: int,
        branch_id: int,
        dept_id: int,
        reporting_to: int | None,
        team_name: str,
        email: str,
        phone_no: str,
        role: str,
        applicant_notes: dict | list | str = None
) -> bool:

    """
    Add a new team member.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :param dept_id: The id of the department that this team member is working in.
    :param branch_id: The id of the branch that this team member is working in.
    :param reporting_to: The id of the senior to whom this team member will report.
    :param team_name: The name of the team member. This is the full display name. Can be the same as others.
    :param email: The email id of the team member.
    :param phone_no: The phone no. of the team member.
    :param role: The role of the team member in the organization.
    :param applicant_notes: Optional notes about the team member.
    :return: True if successful, else False.
    """

    # # Ensure that JSON notes are converted to string:
    # if isinstance(applicant_notes, (list, dict)):
        # applicant_notes = json.to_string(applicant_notes, no_space = True)

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["teamUpdate"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = {
            "branchId": branch_id,
            "idDepartment": dept_id,
            "reportingTo": reporting_to,
            "name": team_name,
            "email": email,
            "phoneNo": phone_no,
            "role": role,
            "applicantNotes": applicant_notes
        }
    )

    # Debugging:
    if response.status_code not in [200]:
        response_json = response.json()
        print("TCAOFF Team-Update:", json.to_string(response_json))

    # If the call succeeded:
    if response.status_code in [200]:
        print(f"TCAOFF: team '{user_id}' updated")
        return True

    # If the call failed:
    else:
        print(f"TCAOFF: team '{user_id}' NOT updated")
        return False


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


def attendance_mark(
        tcaoff_creds: dict,
        user_id: int,
        status: Literal["P", "H", "A", "OT"],
        over_time: int | float,
        attendance_date: datetime.datetime | None = None,
        json_notes: dict = None
) -> bool:

    """
    Add a new department.
    :param tcaoff_creds: The set of credentials to use with the API call.
    :param user_id: The id of the user whose attendance is being marked.
    :param status: The status of the attendance.
        1. "P" for present,
        2. "H" for half-day,
        3. "A" for absent,
        4. "OT" for over-time.
    :param over_time: The amount of over-time work in hours.
    :param attendance_date: The date of the attendance. If not given, today's date will be used.
    :return: True if logged out, else False.
    """

    # Prepare the payload:
    json_payload = {
        "date": (
                date_time.parse_date_time(attendance_date) or
                date_time.get_current_date_time()
        ).strftime("%Y-%m-%d"),
        "idUser": user_id,
        "status": status if over_time <= 0.0 else "OT",
        "ot": over_time,
    }

    # Add JSON notes is needed:
    if json_notes:
        json_payload["jsonNotes"] = json_notes

    # Make the API call:
    response = requests.post(
        url = tcaoff_creds["urls"]["attendanceMark"],
        headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
        json = json_payload
    )

    # Debugging:
    if response.status_code not in [200]:
        try:
            response_json = response.json()
            print("TCAOFF Attendance-Mark:", json.to_string(response_json))
        except Exception as e:
            print("TCAOFF Attendance-Mark:", e)
            print("TCAOFF Attendance-Mark:", response.content)
        print("Payload:", json.to_string(json_payload))

    # If the call succeeded:
    if response.status_code in [200]:
        print(f"TCAOFF: attendance for '{user_id}' marked")
        return True

    # If the call failed:
    else:
        print(f"TCAOFF: attendance for '{user_id}' NOT marked")
        return False


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


if __name__ == "__main__":

    pass
