import {ChildProcess, fork} from 'child_process';
import {join} from 'path';
import {goldenMatcher} from './matcher';
import {Client} from './tsclient';

describe('Angular Language Service', () => {
  jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; /* 10 seconds */
  const PWD = process.env.PWD!;
  const SERVER_PATH = './node_modules/typescript/lib/tsserver.js';
  let server: ChildProcess;
  let client: Client;

  beforeEach(() => {
    jasmine.addMatchers(goldenMatcher);
    server = fork(
        SERVER_PATH,
        [
          '--logVerbosity',
          'verbose',
          '--logFile',
          join(PWD, 'tsserver.log'),
        ],
        {
          stdio: ['pipe', 'pipe', 'inherit', 'ipc'],
        });
    client = new Client(server);
    client.listen();
  });

  afterEach(async () => {
    client.sendRequest('exit', {});

    // Give server process some time to flush all messages
    await new Promise((resolve) => setTimeout(resolve, 1000));
  });

  it('should be launched as tsserver plugin', async () => {
    let response = await client.sendRequest('configure', {
      hostInfo: 'vscode',
    });
    expect(response).toMatchGolden('configure.json');
    response = await client.sendRequest('compilerOptionsForInferredProjects', {
      'options': {
        module: 'CommonJS',
        target: 'ES6',
        allowSyntheticDefaultImports: true,
        allowNonTsExtensions: true,
        allowJs: true,
        jsx: 'Preserve'
      }
    });
    expect(response).toMatchGolden('compilerOptionsForInferredProjects.json');
    // Server does not send response to open request
    // https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1055
    client.sendRequest('open', {
      file: `${PWD}/project/app/app.module.ts`,
    });
    // Server does not send response to geterr request
    // https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts#L1770
    client.sendRequest('geterr', {delay: 0, files: [`${PWD}/project/app/app.module.ts`]});
  });

  it('should perform completions', async () => {
    await client.sendRequest('configure', {
      hostInfo: 'vscode',
    });
    await client.sendRequest('compilerOptionsForInferredProjects', {
      'options': {
        module: 'CommonJS',
        target: 'ES6',
        allowSyntheticDefaultImports: true,
        allowNonTsExtensions: true,
        allowJs: true,
        jsx: 'Preserve'
      }
    });

    client.sendRequest('open', {
      file: `${PWD}/project/app/app.component.ts`,
    });

    client.sendRequest('geterr', {delay: 0, files: [`${PWD}/project/app/app.component.ts`]});

    client.sendRequest('change', {
      file: `${PWD}/project/app/app.component.ts`,
      line: 5,
      offset: 30,
      endLine: 5,
      endOffset: 30,
      insertString: '.',
    });

    const response = await client.sendRequest('completionInfo', {
      file: `${PWD}/project/app/app.component.ts`,
      line: 5,
      offset: 31,
    });
    expect(response).toMatchGolden('completionInfo.json');
  });

  it('should perform quickinfo', async () => {
    client.sendRequest('open', {
      file: `${PWD}/project/app/app.component.ts`,
    });

    const resp1 = await client.sendRequest('reload', {
      file: `${PWD}/project/app/app.component.ts`,
      tmpFile: `${PWD}/project/app/app.component.ts`,
    }) as any;
    expect(resp1.command).toBe('reload');
    expect(resp1.success).toBe(true);

    const resp2 = await client.sendRequest('quickinfo', {
      file: `${PWD}/project/app/app.component.ts`,
      line: 5,
      offset: 28,
    });
    expect(resp2).toMatchGolden('quickinfo.json');
  });

  it('should perform definition', async () => {
    client.sendRequest('open', {
      file: `${PWD}/project/app/app.component.ts`,
    });

    const resp1 = await client.sendRequest('reload', {
      file: `${PWD}/project/app/app.component.ts`,
      tmpFile: `${PWD}/project/app/app.component.ts`,
    }) as any;
    expect(resp1.command).toBe('reload');
    expect(resp1.success).toBe(true);

    const resp2 = await client.sendRequest('definition', {
      file: `${PWD}/project/app/app.component.ts`,
      line: 5,
      offset: 28,
    });
    expect(resp2).toMatchGolden('definition.json');
  });

  it('should perform definitionAndBoundSpan', async () => {
    client.sendRequest('open', {
      file: `${PWD}/project/app/app.component.ts`,
    });

    const resp1 = await client.sendRequest('reload', {
      file: `${PWD}/project/app/app.component.ts`,
      tmpFile: `${PWD}/project/app/app.component.ts`,
    }) as any;
    expect(resp1.command).toBe('reload');
    expect(resp1.success).toBe(true);

    const resp2 = await client.sendRequest('definitionAndBoundSpan', {
      file: `${PWD}/project/app/app.component.ts`,
      line: 5,
      offset: 28,
    });
    expect(resp2).toMatchGolden('definitionAndBoundSpan.json');
  });

  it('should perform definitionAndBoundSpan for template URLs', async () => {
    client.sendRequest('open', {
      file: `${PWD}/project/app/widget.component.ts`,
    });

    const resp1 = await client.sendRequest('reload', {
      file: `${PWD}/project/app/widget.component.ts`,
      tmpFile: `${PWD}/project/app/widget.component.ts`,
    }) as any;
    expect(resp1.command).toBe('reload');
    expect(resp1.success).toBe(true);

    const resp2 = await client.sendRequest('definitionAndBoundSpan', {
      file: `${PWD}/project/app/widget.component.ts`,
      line: 5,
      offset: 19,
    });
    expect(resp2).toMatchGolden('templateUrlDefinition.json');
  });

  it('should perform definitionAndBoundSpan for style URLs', async () => {
    client.sendRequest('open', {
      file: `${PWD}/project/app/widget.component.ts`,
    });
    client.sendRequest('open', {
      file: `${PWD}/project/app/style.css`,
    });

    const resp1 = await client.sendRequest('reload', {
      file: `${PWD}/project/app/widget.component.ts`,
      tmpFile: `${PWD}/project/app/widget.component.ts`,
    }) as any;
    expect(resp1.command).toBe('reload');
    expect(resp1.success).toBe(true);

    const resp2 = await client.sendRequest('definitionAndBoundSpan', {
      file: `${PWD}/project/app/widget.component.ts`,
      line: 6,
      offset: 18,
    });
    expect(resp2).toMatchGolden('styleUrlsDefinition.json');
  });
});