"""Initial waitlist construction."""

import math
from typing import Any, List, NoReturn

# TODO: Change `typing.NoReturn` to `typing.Never` when Python 3.11 is oldest LTS.

import ciw

# TODO: Change `typing.NoReturn` to `typing.Never` when Python 3.11 is oldest LTS.
def begin_service_if_possible_accept(
    node: ciw.Node, next_individual: ciw.Individual
) -> NoReturn:
    """Begins the service of the next individual (at acceptance point):
    - Sets the arrival date as the current time
    - If there is a free server or there are infinite servers:
        - Attach the server to the individual (only when servers are not infinite)
    - Get service start time, service time, service end time
    - Update the server's end date (only when servers are not infinite)
    """
    free_server = node.find_free_server(next_individual)
    if free_server is None and math.isinf(node.c) is False:
        node.decide_preempt(next_individual)
    if free_server is not None or math.isinf(node.c):
        if math.isinf(node.c) is False:
            node.attach_server(free_server, next_individual)
        next_individual.service_start_date = 0
        next_individual.service_time = node.get_service_time(next_individual)
        next_individual.service_end_date = next_individual.service_time
        if not math.isinf(node.c):
            free_server.next_end_service_date = next_individual.service_end_date


# from the ciw source code; needs to be adapted to accommodate the pre-existing wait list
def accept(
    individual_id: ciw.Individual,
    individual_class: Any,
    arrival_date: float,
    node: ciw.Node,
    simulation: ciw.Simulation,
) -> NoReturn:
    """Accepts a new customer to the queue:
    - record all other information at arrival point
    - update state tracker
    """
    simulation.current_time = arrival_date
    next_individual = ciw.Individual(
        id_number=individual_id,
        customer_class=individual_class,
        priority_class=simulation.network.priority_class_mapping[individual_class],
        simulation=simulation,
    )
    next_individual.node = node.id_number
    next_individual.queue_size_at_arrival = "Unknown"
    node.individuals[next_individual.priority_class].append(next_individual)
    node.number_of_individuals += 1
    node.simulation.statetracker.change_state_accept(node, next_individual)
    next_individual.arrival_date = arrival_date
    begin_service_if_possible_accept(node, next_individual)

    simulation.nodes[0].number_of_individuals += 1
    simulation.nodes[0].number_of_individuals_per_class[
        next_individual.customer_class
    ] += 1
    simulation.nodes[0].number_accepted_individuals += 1
    simulation.nodes[0].number_accepted_individuals_per_class[
        next_individual.customer_class
    ] += 1


def create_existing_customers_from_list(
    backlog: List[list], simulation: ciw.Simulation
) -> NoReturn:
    """Occupies instance of simulation with individuals.

    PARAMETERS
    ----------
    backlog (list): List of data for each individual to be added.
    simulation (ciw.Simulation): Instance of ciw.Simulation.
    """
    customer_count = 1
    for row in backlog:
        customer_id = row[0]
        customer_class = row[1]
        customer_arrival_date = row[2]
        customer_node = row[3]
        accept(
            customer_id,
            customer_class,
            customer_arrival_date,
            simulation.nodes[customer_node],
            simulation,
        )
        customer_count += 1
