import * as interfaces from '../ts_interfaces/index.js';
import type { IRouteConfig } from '@push.rocks/smartproxy';
import type { DcRouterApiClient } from './classes.dcrouterapiclient.js';

export class Route {
  private clientRef: DcRouterApiClient;

  // Data from IMergedRoute
  public routeConfig: IRouteConfig;
  public id: string;
  public enabled: boolean;
  public origin: 'config' | 'email' | 'dns' | 'api';
  public createdAt?: number;
  public updatedAt?: number;

  // Convenience accessors
  public get name(): string {
    return this.routeConfig.name || '';
  }

  constructor(clientRef: DcRouterApiClient, data: interfaces.data.IMergedRoute) {
    this.clientRef = clientRef;
    this.routeConfig = data.route;
    this.id = data.id;
    this.enabled = data.enabled;
    this.origin = data.origin;
    this.createdAt = data.createdAt;
    this.updatedAt = data.updatedAt;
  }

  public async update(changes: Partial<IRouteConfig>): Promise<void> {
    const response = await this.clientRef.request<interfaces.requests.IReq_UpdateRoute>(
      'updateRoute',
      this.clientRef.buildRequestPayload({ id: this.id, route: changes }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to update route');
    }
  }

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

  public async toggle(enabled: boolean): Promise<void> {
    const response = await this.clientRef.request<interfaces.requests.IReq_ToggleRoute>(
      'toggleRoute',
      this.clientRef.buildRequestPayload({ id: this.id, enabled }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to toggle route');
    }
    this.enabled = enabled;
  }
}

export class RouteBuilder {
  private clientRef: DcRouterApiClient;
  private routeConfig: Partial<IRouteConfig> = {};
  private isEnabled: boolean = true;

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

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

  public setMatch(match: IRouteConfig['match']): this {
    this.routeConfig.match = match;
    return this;
  }

  public setAction(action: IRouteConfig['action']): this {
    this.routeConfig.action = action;
    return this;
  }

  public setTls(tls: IRouteConfig['action']['tls']): this {
    if (!this.routeConfig.action) {
      this.routeConfig.action = { type: 'forward' } as IRouteConfig['action'];
    }
    this.routeConfig.action!.tls = tls;
    return this;
  }

  public setEnabled(enabled: boolean): this {
    this.isEnabled = enabled;
    return this;
  }

  public async save(): Promise<Route> {
    const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
      'createRoute',
      this.clientRef.buildRequestPayload({
        route: this.routeConfig as IRouteConfig,
        enabled: this.isEnabled,
      }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to create route');
    }

    // Return a Route instance by re-fetching the list
    const { routes } = await new RouteManager(this.clientRef).list();
    const created = routes.find((r) => r.id === response.routeId);
    if (created) {
      return created;
    }

    // Fallback: construct from known data
    return new Route(this.clientRef, {
      route: this.routeConfig as IRouteConfig,
      id: response.routeId || '',
      enabled: this.isEnabled,
      origin: 'api',
    });
  }
}

export class RouteManager {
  private clientRef: DcRouterApiClient;

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

  public async list(): Promise<{ routes: Route[]; warnings: interfaces.data.IRouteWarning[] }> {
    const response = await this.clientRef.request<interfaces.requests.IReq_GetMergedRoutes>(
      'getMergedRoutes',
      this.clientRef.buildRequestPayload() as any,
    );
    return {
      routes: response.routes.map((r) => new Route(this.clientRef, r)),
      warnings: response.warnings,
    };
  }

  public async create(routeConfig: IRouteConfig, enabled?: boolean): Promise<Route> {
    const response = await this.clientRef.request<interfaces.requests.IReq_CreateRoute>(
      'createRoute',
      this.clientRef.buildRequestPayload({ route: routeConfig, enabled: enabled ?? true }) as any,
    );
    if (!response.success) {
      throw new Error(response.message || 'Failed to create route');
    }
    return new Route(this.clientRef, {
      route: routeConfig,
      id: response.routeId || '',
      enabled: enabled ?? true,
      origin: 'api',
    });
  }

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