/**
 * Integration tests for workspace CLI flags
 */

import { spawnSync } from 'child_process';
import { mkdtempSync, writeFileSync, rmSync, mkdirSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

describe('Workspace CLI Integration', () => {
  let tempDir: string;
  const projectRoot = join(__dirname, '../..');
  const claudelintBin = join(projectRoot, 'bin/claudelint');

  beforeEach(() => {
    tempDir = mkdtempSync(join(tmpdir(), 'claudelint-cli-test-'));
  });

  afterEach(() => {
    rmSync(tempDir, { recursive: true, force: true });
  });

  /**
   * Helper to execute CLI command.
   * Returns combined stdout+stderr as 'stdout' for backwards compatibility,
   * plus separate streams for targeted assertions.
   */
  function runCLI(args: string, expectError = false): { stdout: string; stderr: string; code: number } {
    const argList = args.split(' ');
    const result = spawnSync(claudelintBin, argList, {
      cwd: tempDir,
      encoding: 'utf-8',
    });

    const code = result.status ?? 1;

    if (!expectError && code !== 0) {
      throw new Error(
        `CLI exited with code ${code}.\nstdout: ${result.stdout}\nstderr: ${result.stderr}`
      );
    }

    // Combine stdout+stderr for backwards-compatible assertions
    return {
      stdout: (result.stdout || '') + (result.stderr || ''),
      stderr: result.stderr || '',
      code,
    };
  }

  /**
   * Create a pnpm workspace fixture
   */
  function createPnpmWorkspace() {
    // Root workspace config
    writeFileSync(
      join(tempDir, 'pnpm-workspace.yaml'),
      'packages:\n  - "packages/*"\n'
    );

    // Root config
    writeFileSync(
      join(tempDir, '.claudelintrc.json'),
      JSON.stringify({
        rules: {
          'claude-md-size': 'error',
        },
      })
    );

    // Create packages
    mkdirSync(join(tempDir, 'packages'));

    // Package 1 (valid)
    mkdirSync(join(tempDir, 'packages', 'app-1'));
    writeFileSync(
      join(tempDir, 'packages', 'app-1', '.claudelintrc.json'),
      JSON.stringify({
        extends: '../../.claudelintrc.json',
      })
    );

    // Package 2 (valid)
    mkdirSync(join(tempDir, 'packages', 'app-2'));
    writeFileSync(
      join(tempDir, 'packages', 'app-2', '.claudelintrc.json'),
      JSON.stringify({
        extends: '../../.claudelintrc.json',
      })
    );

    // Package 3 (valid)
    mkdirSync(join(tempDir, 'packages', 'shared'));
    writeFileSync(
      join(tempDir, 'packages', 'shared', '.claudelintrc.json'),
      JSON.stringify({
        extends: '../../.claudelintrc.json',
      })
    );
  }

  /**
   * Create workspace with validation errors
   */
  function createWorkspaceWithErrors() {
    createPnpmWorkspace();

    // Add CLAUDE.md to app-1 that's too large (will error)
    const largeContent = 'a'.repeat(100000); // 100KB (over 50KB limit)
    writeFileSync(
      join(tempDir, 'packages', 'app-1', 'CLAUDE.md'),
      largeContent
    );

    // Add valid CLAUDE.md to app-2
    writeFileSync(
      join(tempDir, 'packages', 'app-2', 'CLAUDE.md'),
      '# Test\n\nValid content.'
    );
  }

  describe('--workspace flag', () => {
    it('validates specific package by name', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspace app-1');

      expect(result.code).toBe(0);
      expect(result.stdout).toContain('app-1');
    });

    it('errors when package not found', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspace nonexistent', true);

      expect(result.code).toBe(2);
      const output = result.stdout + result.stderr;
      expect(output).toContain('Available packages:');
      expect(output).toContain('app-1');
      expect(output).toContain('app-2');
      expect(output).toContain('shared');
    });

    it('displays available packages on error', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspace missing-pkg', true);

      expect(result.code).toBe(2);
      expect(result.stdout).toContain('Available packages:');
      // Should list all 3 packages
      const lines = result.stdout.split('\n');
      const packageLines = lines.filter(l => l.includes('app-1') || l.includes('app-2') || l.includes('shared'));
      expect(packageLines.length).toBeGreaterThanOrEqual(3);
    });

    it('errors when no workspace detected', () => {
      // No workspace config in tempDir

      const result = runCLI('check-all --workspace app-1', true);

      expect(result.code).toBe(2);
      // Error message goes to logger which may be formatted/colored
      // Just verify the command exits with error code 2
    });

    it('validates package in its directory context', () => {
      createWorkspaceWithErrors();

      // Validate app-1 (has error)
      const result = runCLI('check-all --workspace app-1', true);

      expect(result.code).toBe(1); // Should fail due to size error
      expect(result.stdout).toContain('app-1');
      expect(result.stdout).toContain('claude-md-size');
    });
  });

  describe('--workspaces flag', () => {
    it('validates all packages', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspaces');

      expect(result.code).toBe(0);
      expect(result.stdout).toContain('Validating 3 workspace packages');
      expect(result.stdout).toContain('app-1');
      expect(result.stdout).toContain('app-2');
      expect(result.stdout).toContain('shared');
    });

    it('aggregates errors across packages', () => {
      createWorkspaceWithErrors();

      const result = runCLI('check-all --workspaces', true);

      expect(result.code).toBe(1); // Should fail due to app-1 error
      expect(result.stdout).toContain('Workspace Summary');
      expect(result.stdout).toContain('Total packages: 3');
      expect(result.stdout).toContain('Failed packages: 1');
    });

    it('shows workspace summary', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspaces');

      expect(result.stdout).toContain('Workspace Summary');
      expect(result.stdout).toContain('Total packages:');
      expect(result.stdout).toContain('Failed packages:');
      expect(result.stdout).toContain('Total errors:');
      expect(result.stdout).toContain('Total warnings:');
    });

    it('exit code is 1 when any package fails', () => {
      createWorkspaceWithErrors();

      const result = runCLI('check-all --workspaces', true);

      expect(result.code).toBe(1);
      expect(result.stdout).toContain('Failed packages: 1');
    });

    it('exit code is 0 when all packages pass', () => {
      createPnpmWorkspace();

      const result = runCLI('check-all --workspaces');

      expect(result.code).toBe(0);
      expect(result.stdout).toContain('Failed packages: 0');
    });

    it('displays failed packages list', () => {
      createWorkspaceWithErrors();

      const result = runCLI('check-all --workspaces', true);

      expect(result.stdout).toContain('Failed packages:');
      expect(result.stdout).toContain('app-1'); // Only app-1 should fail
    });

    it('errors when no workspace detected', () => {
      // No workspace config

      const result = runCLI('check-all --workspaces', true);

      expect(result.code).toBe(2);
      // Error message goes to logger which may be formatted/colored
      // Just verify the command exits with error code 2
    });
  });

  describe('npm workspaces', () => {
    it('detects npm workspaces from package.json', () => {
      // Create npm workspace
      writeFileSync(
        join(tempDir, 'package.json'),
        JSON.stringify({
          name: 'monorepo',
          workspaces: ['packages/*'],
        })
      );

      mkdirSync(join(tempDir, 'packages'));
      mkdirSync(join(tempDir, 'packages', 'lib-1'));
      writeFileSync(
        join(tempDir, 'packages', 'lib-1', '.claudelintrc.json'),
        '{}'
      );

      const result = runCLI('check-all --workspaces');

      expect(result.code).toBe(0);
      expect(result.stdout).toContain('Validating 1 workspace packages');
      expect(result.stdout).toContain('lib-1');
    });
  });

  describe('workspace root auto-detection', () => {
    it('works from nested package directory with --workspace', () => {
      createPnpmWorkspace();

      // Change to nested package directory
      const nestedDir = join(tempDir, 'packages', 'app-1');
      mkdirSync(nestedDir, { recursive: true });

      const result = runCLI(`check-all --workspace app-1`, false);

      expect(result.code).toBe(0);
      expect(result.stdout).toContain('app-1');
    });

    it('works from nested package directory with --workspaces', () => {
      createPnpmWorkspace();

      // Change to nested package directory
      const nestedDir = join(tempDir, 'packages', 'app-2');
      mkdirSync(nestedDir, { recursive: true });

      // Run from nested directory
      const result = spawnSync(
        join(__dirname, '../..', 'bin/claudelint'),
        ['check-all', '--workspaces'],
        { cwd: nestedDir, encoding: 'utf-8' }
      );

      const output = (result.stdout || '') + (result.stderr || '');

      if (result.status === 0) {
        expect(output).toContain('Validating 3 workspace packages');
        expect(output).toContain('app-1');
        expect(output).toContain('app-2');
        expect(output).toContain('shared');
      } else {
        // If command fails, check if it at least found the workspace
        expect(output).toContain('workspace');
      }
    });

    it('works from deeply nested directory', () => {
      createPnpmWorkspace();

      // Create deeply nested directory
      const deeplyNested = join(tempDir, 'packages', 'app-1', 'src', 'components');
      mkdirSync(deeplyNested, { recursive: true });

      // Run from deeply nested directory
      const result = spawnSync(
        join(__dirname, '../..', 'bin/claudelint'),
        ['check-all', '--workspaces'],
        { cwd: deeplyNested, encoding: 'utf-8' }
      );

      const output = (result.stdout || '') + (result.stderr || '');
      expect(output).toContain('workspace');
    });

    it('shows error when run from non-workspace directory', () => {
      // No workspace config
      const result = runCLI('check-all --workspaces', true);

      expect(result.code).toBe(2);
      const output = result.stdout + result.stderr;
      expect(output).toContain('No workspace detected');
    });
  });

  describe('edge cases', () => {
    it('handles empty workspace (no packages)', () => {
      writeFileSync(
        join(tempDir, 'pnpm-workspace.yaml'),
        'packages:\n  - "packages/*"\n'
      );

      // Create packages dir but no packages
      mkdirSync(join(tempDir, 'packages'));

      const result = runCLI('check-all --workspaces');

      expect(result.stdout).toContain('Validating 0 workspace packages');
    });

    it('handles package with no config', () => {
      writeFileSync(
        join(tempDir, 'pnpm-workspace.yaml'),
        'packages:\n  - "packages/*"\n'
      );

      mkdirSync(join(tempDir, 'packages'));
      mkdirSync(join(tempDir, 'packages', 'app-1'));
      // No .claudelintrc.json in app-1

      const result = runCLI('check-all --workspaces');

      // Should still validate (uses defaults)
      expect(result.code).toBe(0);
      expect(result.stdout).toContain('app-1');
    });
  });
});
