to update the other libs, clients etc as well","pastedContents":{},"timestamp":1768760271820,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"we'll need to update the other libs, clients etc as well and check obsidian on the dwm blocks howhighami ld block","pastedContents":{},"timestamp":1768760304620,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"deploy the api","pastedContents":{},"timestamp":1768760893111,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"deploy the discord + irc bot","pastedContents":{},"timestamp":1768761258918,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"resume","pastedContents":{},"timestamp":1768761724573,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"fix the up arrow and tab auto complete","pastedContents":{},"timestamp":1768762187252,"project":"/home/josie/development/personal/josiedot/health","sessionId":"edc2a04c-e0b6-405d-a9d1-2a4d1e725563"}
{"display":"can you write a prompt for claude code in alpm.rs to implement the gap we think exists/verify that it would make sense to implment this in alp.rs","pastedContents":{},"timestamp":1768840821444,"project":"/home/josie/development/archlinux/cockpit-pacman","sessionId":"eed13a7d-58bd-4c29-8cee-83361e3625e7"}
{"display":"[Pasted text #1 +57 lines]","pastedContents":{"1":{"id":1,"type":"text","contentHash":"0a8b0e1fa58f89af"}},"timestamp":1768840927403,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"yes, implement it","pastedContents":{},"timestamp":1768841332471,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"[Pasted text #1 +27 lines]\n\ntry using the local code: /home/josie/development/archlinux/packaging/alpm.rs","pastedContents":{"1":{"id":1,"type":"text","contentHash":"0d6cc58ba7e2ebcf"}},"timestamp":1768841641316,"project":"/home/josie/development/archlinux/cockpit-pacman","sessionId":"eed13a7d-58bd-4c29-8cee-83361e3625e7"}
{"display":"commit this","pastedContents":{},"timestamp":1768841996091,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"can you bring out the critical review agent to review our change","pastedContents":{},"timestamp":1768842412993,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"leave it, matches existing patterns but impleement add_pkg_by_name and add safety comments ","pastedContents":{},"timestamp":1768842854562,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"amend the last commit and edit the commit message","pastedContents":{},"timestamp":1768843114954,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"do a final critical review before we submit this pr. look for overengineering, overly verbose comments in particular","pastedContents":{},"timestamp":1768843247316,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"strip them","pastedContents":{},"timestamp":1768843632397,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"write a pr comment as well","pastedContents":{},"timestamp":1768843933191,"project":"/home/josie/development/archlinux/packaging/alpm.rs","sessionId":"6a0035a9-ed9f-4797-ba41-2cbaf498b070"}
{"display":"write the draft to a file instead","pastedContents":{},"timestamp":1768844175615,"project":"/home/josie//*
 * Copyright 2019 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

import { ChannelOptions } from './channel-options';
import { Endpoint, SubchannelAddress } from './subchannel-address';
import { ConnectivityState } from './connectivity-state';
import { Picker } from './picker';
import type { ChannelRef, SubchannelRef } from './channelz';
import { SubchannelInterface } from './subchannel-interface';
import { LoadBalancingConfig } from './service-config';
import { log } from './logging';
import { LogVerbosity } from './constants';
import { ChannelCredentials } from './channel-credentials';

/**
 * A collection of functions associated with a channel that a load balancer
 * can call as necessary.
 */
export interface ChannelControlHelper {
  /**
   * Returns a subchannel connected to the specified address.
   * @param subchannelAddress The address to connect to
   * @param subchannelArgs Extra channel arguments specified by the load balancer
   */
  createSubchannel(
    subchannelAddress: SubchannelAddress,
    subchannelArgs: ChannelOptions,
    credentialsOverride: ChannelCredentials | null
  ): SubchannelInterface;
  /**
   * Passes a new subchannel picker up to the channel. This is called if either
   * the connectivity state changes or if a different picker is needed for any
   * other reason.
   * @param connectivityState New connectivity state
   * @param picker New picker
   */
  updateState(connectivityState: ConnectivityState, picker: Picker): void;
  /**
   * Request new data from the resolver.
   */
  requestReresolution(): void;
  addChannelzChild(child: ChannelRef | SubchannelRef): void;
  removeChannelzChild(child: ChannelRef | SubchannelRef): void;
}

/**
 * Create a child ChannelControlHelper that overrides some methods of the
 * parent while letting others pass through to the parent unmodified. This
 * allows other code to create these children without needing to know about
 * all of the methods to be passed through.
 * @param parent
 * @param overrides
 */
export function createChildChannelControlHelper(
  parent: ChannelControlHelper,
  overrides: Partial<ChannelControlHelper>
): ChannelControlHelper {
  return {
    createSubchannel:
      overrides.createSubchannel?.bind(overrides) ??
      parent.createSubchannel.bind(parent),
    updateState:
      overrides.updateState?.bind(overrides) ?? parent.updateState.bind(parent),
    requestReresolution:
      overrides.requestReresolution?.bind(overrides) ??
      parent.requestReresolution.bind(parent),
    addChannelzChild:
      overrides.addChannelzChild?.bind(overrides) ??
      parent.addChannelzChild.bind(parent),
    removeChannelzChild:
      overrides.removeChannelzChild?.bind(overrides) ??
      parent.removeChannelzChild.bind(parent),
  };
}

/**
 * Tracks one or more connected subchannels and determines which subchannel
 * each request should use.
 */
export interface LoadBalancer {
  /**
   * Gives the load balancer a new list of addresses to start connecting to.
   * The load balancer will start establishing connections with the new list,
   * but will continue using any existing connections until the new connections
   * are established
   * @param endpointList The new list of addresses to connect to
   * @param lbConfig The load balancing config object from the service config,
   *     if one was provided
   */
  updateAddressList(
    endpointList: Endpoint[],
    lbConfig: TypedLoadBalancingConfig,
    attributes: { [key: string]: unknown }
  ): void;
  /**
   * If the load balancer is currently in the IDLE state, start connecting.
   */
  exitIdle(): void;
  /**
   * If the load balancer is currently in the CONNECTING or TRANSIENT_FAILURE
   * state, reset the current connection backoff timeout to its base value and
   * transition to CONNECTING if in TRANSIENT_FAILURE.
   */
  resetBackoff(): void;
  /**
   * The load balancer unrefs all of its subchannels and stops calling methods
   * of its channel control helper.
   */
  destroy(): void;
  /**
   * Get the type name for this load balancer type. Must be constant across an
   * entire load balancer implementation class and must match the name that the
   * balancer implementation class was registered with.
   */
  getTypeName(): string;
}

export interface LoadBalancerConstructor {
  new (
    channelControlHelper: ChannelControlHelper,
    credentials: ChannelCredentials,
    options: ChannelOptions
  ): LoadBalancer;
}

export interface TypedLoadBalancingConfig {
  getLoadBalancerName(): string;
  toJsonObject(): object;
}

export interface TypedLoadBalancingConfigConstructor {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  new (...args: any): TypedLoadBalancingConfig;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  createFromJson(obj: any): TypedLoadBalancingConfig;
}

const registeredLoadBalancerTypes: {
  [name: string]: {
    LoadBalancer: LoadBalancerConstructor;
    LoadBalancingConfig: TypedLoadBalancingConfigConstructor;
  };
} = {};

let defaultLoadBalancerType: string | null = null;

export function registerLoadBalancerType(
  typeName: string,
  loadBalancerType: LoadBalancerConstructor,
  loadBalancingConfigType: TypedLoadBalancingConfigConstructor
) {
  registeredLoadBalancerTypes[typeName] = {
    LoadBalancer: loadBalancerType,
    LoadBalancingConfig: loadBalancingConfigType,
  };
}

export function registerDefaultLoadBalancerType(typeName: string) {
  defaultLoadBalancerType = typeName;
}

export function createLoadBalancer(
  config: TypedLoadBalancingConfig,
  channelControlHelper: ChannelControlHelper,
  credentials: ChannelCredentials,
  options: ChannelOptions
): LoadBalancer | null {
  const typeName = config.getLoadBalancerName();
  if (typeName in registeredLoadBalancerTypes) {
    return new registeredLoadBalancerTypes[typeName].LoadBalancer(
      channelControlHelper,
      credentials,
      options
    );
  } else {
    return null;
  }
}

export function isLoadBalancerNameRegistered(typeName: string): boolean {
  return typeName in registeredLoadBalancerTypes;
}

export function parseLoadBalancingConfig(
  rawConfig: LoadBalancingConfig
): TypedLoadBalancingConfig {
  const keys = Object.keys(rawConfig);
  if (keys.length !== 1) {
    throw new Error(
      'Provided load balancing config has multiple conflicting entries'
    );
  }
  const typeName = keys[0];
  if (typeName in registeredLoadBalancerTypes) {
    try {
      return registeredLoadBalancerTypes[
        typeName
      ].LoadBalancingConfig.createFromJson(rawConfig[typeName]);
    } catch (e) {
      throw new Error(`${typeName}: ${(e as Error).message}`);
    }
  } else {
    throw new Error(`Unrecognized load balancing config name ${typeName}`);
  }
}

export function getDefaultConfig() {
  if (!defaultLoadBalancerType) {
    throw new Error('No default load balancer type registered');
  }
  return new registeredLoadBalancerTypes[
    defaultLoadBalancerType
  ]!.LoadBalancingConfig();
}

export function selectLbConfigFromList(
  configs: LoadBalancingConfig[],
  fallbackTodefault = false
): TypedLoadBalancingConfig | null {
  for (const config of configs) {
    try {
      return parseLoadBalancingConfig(config);
    } catch (e) {
      log(
        LogVerbosity.DEBUG,
        'Config parsing failed with error',
        (e as Error).message
      );
      continue;
    }
  }
  if (fallbackTodefault) {
    if (defaultLoadBalancerType) {
      return new registeredLoadBalancerTypes[
        defaultLoadBalancerType
      ]!.LoadBalancingConfig();
    } else {
      return null;
    }
  } else {
    return null;
  }
}
