import * as plugins from '../plugins.js';
import { GatewayClientDoc } from '../db/index.js';
import type { IGatewayClient } from '../../ts_interfaces/data/workhoster.js';

const defaultCapabilities: IGatewayClient['capabilities'] = {
  readDomains: true,
  readDnsRecords: true,
  syncRoutes: true,
  syncDnsRecords: false,
  requestCertificates: false,
};

export class GatewayClientManager {
  public async initialize(): Promise<void> {}

  public async listClients(): Promise<IGatewayClient[]> {
    const docs = await GatewayClientDoc.findAll();
    return docs.map((doc) => this.toPublicClient(doc));
  }

  public async getClient(id: string): Promise<IGatewayClient | null> {
    const doc = await GatewayClientDoc.findById(id);
    return doc ? this.toPublicClient(doc) : null;
  }

  public async createClient(options: {
    id?: string;
    type: IGatewayClient['type'];
    name: string;
    description?: string;
    hostnamePatterns?: string[];
    allowedRouteTargets?: IGatewayClient['allowedRouteTargets'];
    capabilities?: IGatewayClient['capabilities'];
    createdBy: string;
  }): Promise<IGatewayClient> {
    const id = this.normalizeId(options.id || `${options.type}-${plugins.uuid.v4()}`);
    if (!id) {
      throw new Error('gateway client id is required');
    }
    if (await GatewayClientDoc.findById(id)) {
      throw new Error('gateway client already exists');
    }

    const now = Date.now();
    const doc = new GatewayClientDoc();
    doc.id = id;
    doc.type = options.type;
    doc.name = options.name.trim();
    doc.description = options.description?.trim() || undefined;
    doc.hostnamePatterns = this.normalizeStringList(options.hostnamePatterns || []);
    doc.allowedRouteTargets = this.normalizeAllowedRouteTargets(options.allowedRouteTargets || []);
    doc.capabilities = { ...defaultCapabilities, ...(options.capabilities || {}) };
    doc.enabled = true;
    doc.createdAt = now;
    doc.updatedAt = now;
    doc.createdBy = options.createdBy;
    await doc.save();
    return this.toPublicClient(doc);
  }

  public async updateClient(
    id: string,
    patch: Partial<Pick<IGatewayClient, 'name' | 'description' | 'hostnamePatterns' | 'allowedRouteTargets' | 'capabilities' | 'enabled'>>,
  ): Promise<IGatewayClient | null> {
    const doc = await GatewayClientDoc.findById(id);
    if (!doc) return null;
    if (patch.name !== undefined) doc.name = patch.name.trim();
    if (patch.description !== undefined) doc.description = patch.description.trim() || undefined;
    if (patch.hostnamePatterns !== undefined) doc.hostnamePatterns = this.normalizeStringList(patch.hostnamePatterns);
    if (patch.allowedRouteTargets !== undefined) doc.allowedRouteTargets = this.normalizeAllowedRouteTargets(patch.allowedRouteTargets);
    if (patch.capabilities !== undefined) doc.capabilities = { ...defaultCapabilities, ...patch.capabilities };
    if (patch.enabled !== undefined) doc.enabled = patch.enabled;
    doc.updatedAt = Date.now();
    await doc.save();
    return this.toPublicClient(doc);
  }

  public async deleteClient(id: string): Promise<boolean> {
    const doc = await GatewayClientDoc.findById(id);
    if (!doc) return false;
    await doc.delete();
    return true;
  }

  private normalizeId(id: string): string {
    return id.trim().toLowerCase().replace(/[^a-z0-9._-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
  }

  private normalizeStringList(values: string[]): string[] {
    return values.map((value) => value.trim().toLowerCase()).filter(Boolean);
  }

  private normalizeAllowedRouteTargets(targets: IGatewayClient['allowedRouteTargets']): IGatewayClient['allowedRouteTargets'] {
    return targets
      .map((target) => ({
        host: target.host.trim().toLowerCase(),
        ports: target.ports.filter((port) => Number.isInteger(port) && port > 0 && port <= 65535),
      }))
      .filter((target) => target.host && target.ports.length > 0);
  }

  private toPublicClient(doc: GatewayClientDoc): IGatewayClient {
    return {
      id: doc.id,
      type: doc.type,
      name: doc.name,
      description: doc.description,
      hostnamePatterns: doc.hostnamePatterns || [],
      allowedRouteTargets: doc.allowedRouteTargets || [],
      capabilities: doc.capabilities || {},
      enabled: doc.enabled,
      createdAt: doc.createdAt,
      updatedAt: doc.updatedAt,
      createdBy: doc.createdBy,
    };
  }
}
