import * as interfaces from '../ts_interfaces/index.js';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';

export class RemoteIngress {
  private clientRef: DcRouterApiClient;

  // Data from IRemoteIngress
  public id: string;
  public name: string;
  public secret: string;
  public listenPorts: number[];
  public listenPortsUdp?: number[];
  public enabled: boolean;
  public autoDerivePorts: boolean;
  public tags?: string[];
  public createdAt: number;
  public updatedAt: number;
  public effectiveListenPorts?: number[];
  public effectiveListenPortsUdp?: number[];
  public manualPorts?: number[];
  public derivedPorts?: number[];

  constructor(clientRef: DcRouterApiClient, data: interfaces.data.IRemoteIngress) {
    this.clientRef = clientRef;
    this.id = data.id;
    this.name = data.name;
    this.secret = data.secret;
    this.listenPorts = data.listenPorts;
    this.listenPortsUdp = data.listenPortsUdp;
    this.enabled = data.enabled;
    this.autoDerivePorts = data.autoDerivePorts;
    this.tags = data.tags;
    this.createdAt = data.createdAt;
    this.updatedAt = data.updatedAt;
    this.effectiveListenPorts = data.effectiveListenPorts;
    this.effectiveListenPortsUdp = data.effectiveListenPortsUdp;
    this.manualPorts = data.manualPorts;
    this.derivedPorts = data.derivedPorts;
  }

  public async update(changes: {
    name?: string;
    listenPorts?: number[];
    autoDerivePorts?: boolean;
    enabled?: boolean;
    tags?: string[];
  }): Promise<void> {
    const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRemoteIngress>(
      'updateRemoteIngress',
      this.clientRef.buildRequestPayload({ id: this.id, ...changes }) as any,
    );
    if (!response.success) {
      throw new Error('Failed to update remote ingress');
    }
    // Update local state from response
    const edge = response.edge;
    this.name = edge.name;
    this.listenPorts = edge.listenPorts;
    this.listenPortsUdp = edge.listenPortsUdp;
    this.enabled = edge.enabled;
    this.autoDerivePorts = edge.autoDerivePorts;
    this.tags = edge.tags;
    this.updatedAt = edge.updatedAt;
    this.effectiveListenPorts = edge.effectiveListenPorts;
    this.effectiveListenPortsUdp = edge.effectiveListenPortsUdp;
    this.manualPorts = edge.manualPorts;
    this.derivedPorts = edge.derivedPorts;
  }

  public async delete(): Promise<void> {
    const response = await this.clientRef.request<interfaces.requests.IReq_DeleteRemoteIngress>(
      'deleteRemoteIngress',
      this.clientRef.buildRequestPayload({ id: this.id }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to delete remote ingress');
    }
  }

  public async regenerateSecret(): Promise<string> {
    const response = await this.clientRef.request<interfaces.requests.IReq_RegenerateRemoteIngressSecret>(
      'regenerateRemoteIngressSecret',
      this.clientRef.buildRequestPayload({ id: this.id }) as any,
    );
    if (!response.success) {
      throw new Error('Failed to regenerate secret');
    }
    this.secret = response.secret;
    return response.secret;
  }

  public async getConnectionToken(hubHost?: string): Promise<string | undefined> {
    const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressConnectionToken>(
      'getRemoteIngressConnectionToken',
      this.clientRef.buildRequestPayload({ edgeId: this.id, hubHost }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to get connection token');
    }
    return response.token;
  }
}

export class RemoteIngressBuilder {
  private clientRef: DcRouterApiClient;
  private edgeName: string = '';
  private edgeListenPorts?: number[];
  private edgeAutoDerivePorts?: boolean;
  private edgeTags?: string[];

  constructor(clientRef: DcRouterApiClient) {
    this.clientRef = clientRef;
  }

  public setName(name: string): this {
    this.edgeName = name;
    return this;
  }

  public setListenPorts(ports: number[]): this {
    this.edgeListenPorts = ports;
    return this;
  }

  public setAutoDerivePorts(auto: boolean): this {
    this.edgeAutoDerivePorts = auto;
    return this;
  }

  public setTags(tags: string[]): this {
    this.edgeTags = tags;
    return this;
  }

  public async save(): Promise<RemoteIngress> {
    const response = await this.clientRef.request<interfaces.requests.IReq_CreateRemoteIngress>(
      'createRemoteIngress',
      this.clientRef.buildRequestPayload({
        name: this.edgeName,
        listenPorts: this.edgeListenPorts,
        autoDerivePorts: this.edgeAutoDerivePorts,
        tags: this.edgeTags,
      }) as any,
    );
    if (!response.success) {
      throw new Error('Failed to create remote ingress');
    }
    return new RemoteIngress(this.clientRef, response.edge);
  }
}

export class RemoteIngressManager {
  private clientRef: DcRouterApiClient;

  constructor(clientRef: DcRouterApiClient) {
    this.clientRef = clientRef;
  }

  public async list(): Promise<RemoteIngress[]> {
    const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngresses>(
      'getRemoteIngresses',
      this.clientRef.buildRequestPayload() as any,
    );
    return response.edges.map((e) => new RemoteIngress(this.clientRef, e));
  }

  public async getStatuses(): Promise<interfaces.data.IRemoteIngressStatus[]> {
    const response = await this.clientRef.request<interfaces.requests.IReq_GetRemoteIngressStatus>(
      'getRemoteIngressStatus',
      this.clientRef.buildRequestPayload() as any,
    );
    return response.statuses;
  }

  public async create(options: {
    name: string;
    listenPorts?: number[];
    autoDerivePorts?: boolean;
    tags?: string[];
  }): Promise<RemoteIngress> {
    const builder = this.build().setName(options.name);
    if (options.listenPorts) builder.setListenPorts(options.listenPorts);
    if (options.autoDerivePorts !== undefined) builder.setAutoDerivePorts(options.autoDerivePorts);
    if (options.tags) builder.setTags(options.tags);
    return builder.save();
  }

  public build(): RemoteIngressBuilder {
    return new RemoteIngressBuilder(this.clientRef);
  }
}
