import { mkdtempSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { describe, expect, it } from 'bun:test';
import { hydrateImageAnalysisRuntimeStatus } from '../../../../src/utils/hooks/image-analysis-runtime-status';
import type { ImageAnalysisStatus } from '../../../../src/utils/hooks/image-analysis-backend-resolver';

function createStatus(overrides: Partial<ImageAnalysisStatus> = {}): ImageAnalysisStatus {
  return {
    enabled: true,
    supported: true,
    status: 'active',
    backendId: 'ghcp',
    backendDisplayName: 'GitHub Copilot (OAuth)',
    model: 'claude-haiku-4.5',
    resolutionSource: 'profile-backend',
    reason: null,
    shouldPersistHook: true,
    persistencePath: '/tmp/orq.settings.json',
    runtimePath: '/api/provider/ghcp',
    usesCurrentTarget: true,
    usesCurrentAuthToken: true,
    hookInstalled: true,
    sharedHookInstalled: true,
    authReadiness: 'unknown',
    authProvider: 'ghcp',
    authDisplayName: 'GitHub Copilot (OAuth)',
    authReason: 'Auth readiness has not been verified yet.',
    proxyReadiness: 'unknown',
    proxyReason: 'CLIProxy runtime readiness has not been verified yet.',
    effectiveRuntimeMode: 'native-read',
    effectiveRuntimeReason: null,
    profileModel: 'claude-haiku-4.5',
    nativeReadPreference: false,
    nativeImageCapable: true,
    nativeImageReason: 'claude-haiku-4.5 can read images natively.',
    ...overrides,
  };
}

describe('image-analysis-runtime-status', () => {
  it('falls back to native read when provider auth is missing', async () => {
    const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), {
      checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }),
      getProxyTarget: () => ({
        host: '127.0.0.1',
        port: 8317,
        protocol: 'http',
        isRemote: false,
      }),
      initializeAccounts: () => {},
      getAuthStatus: () => ({
        provider: 'ghcp',
        authenticated: false,
        tokenDir: '/tmp/auth',
        tokenFiles: [],
        accounts: [],
        defaultAccount: undefined,
      }),
      isCliproxyRunning: async () => true,
    });

    expect(status.authReadiness).toBe('missing');
    expect(status.effectiveRuntimeMode).toBe('native-read');
    expect(status.effectiveRuntimeReason).toContain('ccs ghcp --auth');
  });

  it('marks an idle local proxy as launchable when auth is ready', async () => {
    const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), {
      checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }),
      getProxyTarget: () => ({
        host: '127.0.0.1',
        port: 8317,
        protocol: 'http',
        isRemote: false,
      }),
      initializeAccounts: () => {},
      getAuthStatus: () => ({
        provider: 'ghcp',
        authenticated: true,
        tokenDir: '/tmp/auth',
        tokenFiles: ['github-copilot-test.json'],
        accounts: [],
        defaultAccount: undefined,
      }),
      isCliproxyRunning: async () => false,
    });

    expect(status.authReadiness).toBe('ready');
    expect(status.proxyReadiness).toBe('stopped');
    expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis');
  });

  it('uses getAuthStatus when initializeAccounts is omitted from an override', async () => {
    const tempCcsHome = mkdtempSync(join(tmpdir(), 'ccs-image-analysis-runtime-status-'));
    const originalCcsHome = process.env.CCS_HOME;
    const originalCcsDir = process.env.CCS_DIR;
    delete process.env.CCS_DIR;
    process.env.CCS_HOME = tempCcsHome;

    try {
      const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), {
        getProxyTarget: () => ({ host: '127.0.0.1', port: 8317, protocol: 'http', isRemote: false }),
        initializeAccounts: undefined as unknown as () => void,
        getAuthStatus: () => ({
          provider: 'ghcp',
          authenticated: true,
          tokenDir: '/tmp/auth',
          tokenFiles: ['github-copilot-test.json'],
          accounts: [],
          defaultAccount: undefined,
        }),
        isCliproxyRunning: async () => true,
      });

      expect(status.authReadiness).toBe('ready');
      expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis');
    } finally {
      if (originalCcsHome === undefined) delete process.env.CCS_HOME;
      else process.env.CCS_HOME = originalCcsHome;

      if (originalCcsDir === undefined) delete process.env.CCS_DIR;
      else process.env.CCS_DIR = originalCcsDir;

      rmSync(tempCcsHome, { recursive: true, force: true });
    }
  });

  it('treats an unreachable remote proxy as unavailable', async () => {
    const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), {
      getProxyTarget: () => ({
        host: 'remote.example',
        port: 443,
        protocol: 'https',
        authToken: 'token',
        managementKey: 'secret',
        isRemote: true,
      }),
      fetchRemoteAuthStatus: async () => [
        {
          provider: 'ghcp',
          displayName: 'GitHub Copilot (OAuth)',
          authenticated: true,
          tokenFiles: 1,
          accounts: [],
          defaultAccount: null,
          source: 'remote',
        },
      ],
      checkRemoteProxy: async () => ({
        reachable: false,
        error: 'Remote CLIProxy target remote.example:443 is unreachable.',
      }),
    });

    expect(status.authReadiness).toBe('ready');
    expect(status.proxyReadiness).toBe('unavailable');
    expect(status.effectiveRuntimeMode).toBe('native-read');
    expect(status.effectiveRuntimeReason).toContain('remote.example:443');
  });

  it('keeps hook-missing as a degraded fallback state while primary runtime stays active', async () => {
    const status = await hydrateImageAnalysisRuntimeStatus(
      createStatus({
        status: 'hook-missing',
        reason: 'Profile hook is missing from the persisted settings file.',
      }),
      {
        checkRemoteProxy: async () => ({ reachable: false, error: 'not-used' }),
        getProxyTarget: () => ({
          host: '127.0.0.1',
          port: 8317,
          protocol: 'http',
          isRemote: false,
        }),
        initializeAccounts: () => {},
        getAuthStatus: () => ({
          provider: 'ghcp',
          authenticated: true,
          tokenDir: '/tmp/auth',
          tokenFiles: ['github-copilot-test.json'],
          accounts: [],
          defaultAccount: undefined,
        }),
        isCliproxyRunning: async () => true,
      }
    );

    expect(status.authReadiness).toBe('ready');
    expect(status.proxyReadiness).toBe('ready');
    expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis');
    expect(status.effectiveRuntimeReason).toContain('Profile hook is missing');
  });

  it('marks a reachable remote proxy as remote instead of inspecting local 8317', async () => {
    const status = await hydrateImageAnalysisRuntimeStatus(createStatus(), {
      getProxyTarget: () => ({
        host: 'remote.example',
        port: 443,
        protocol: 'https',
        authToken: 'token',
        managementKey: 'secret',
        allowSelfSigned: true,
        isRemote: true,
      }),
      fetchRemoteAuthStatus: async () => [
        {
          provider: 'ghcp',
          displayName: 'GitHub Copilot (OAuth)',
          authenticated: true,
          tokenFiles: 1,
          accounts: [],
          defaultAccount: null,
          source: 'remote',
        },
      ],
      checkRemoteProxy: async () => ({
        reachable: true,
        latencyMs: 42,
      }),
      isCliproxyRunning: async () => false,
    });

    expect(status.proxyReadiness).toBe('remote');
    expect(status.proxyReason).toContain('remote.example:443');
    expect(status.effectiveRuntimeMode).toBe('cliproxy-image-analysis');
  });
});
