// Copyright 2022 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

import { CompletionContext } from '@codemirror/autocomplete';
import { syntaxTree } from '@codemirror/language';
import { Device } from 'pigweedjs';

const completePropertyAfter = ['PropertyName', '.', '?.'];
const dontCompleteIn = [
  'TemplateString',
  'LineComment',
  'BlockComment',
  'VariableDefinition',
  'PropertyDefinition',
];
// eslint-disable-next-line @typescript-eslint/no-var-requires
const objectPath = require('object-path');

export function completeFromGlobalScope(context: CompletionContext) {
  const nodeBefore = syntaxTree(context.state).resolveInner(context.pos, -1);

  if (
    completePropertyAfter.includes(nodeBefore.name) &&
    nodeBefore.parent?.name == 'MemberExpression'
  ) {
    const object = nodeBefore.parent.getChild('Expression');
    if (object?.name == 'VariableName') {
      const from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from;
      const variableName = context.state.sliceDoc(object.from, object.to);
      // @ts-ignore
      if (typeof window[variableName] == 'object') {
        // @ts-ignore
        return completeProperties(from, window[variableName]);
      }
    } else if (object?.name == 'MemberExpression') {
      const from = /\./.test(nodeBefore.name) ? nodeBefore.to : nodeBefore.from;
      const variableName = context.state.sliceDoc(object.from, object.to);
      const variable = resolveWindowVariable(variableName);
      // @ts-ignore
      if (typeof variable == 'object') {
        // @ts-ignore
        return completeProperties(from, variable, variableName);
      }
    }
  } else if (nodeBefore.name == 'VariableName') {
    return completeProperties(nodeBefore.from, window);
  } else if (context.explicit && !dontCompleteIn.includes(nodeBefore.name)) {
    return completeProperties(context.pos, window);
  }
  return null;
}

function completeProperties(
  from: number,
  object: object,
  variableName?: string,
) {
  const options = [];
  for (const name in object) {
    // @ts-ignore
    if (object[name] instanceof Function && variableName) {
      // eslint-disable-next-line no-debugger
      debugger;
      options.push({
        label: name,
        // @ts-ignore
        detail: getFunctionDetailText(`${variableName}.${name}`),
        type: 'function',
      });
    } else {
      options.push({
        label: name,
        type: 'variable',
      });
    }
  }
  return {
    from,
    options,
    validFor: /^[\w$]*$/,
  };
}

function resolveWindowVariable(variableName: string) {
  if (objectPath.has(window, variableName)) {
    return objectPath.get(window, variableName);
  }
}

function getFunctionDetailText(fullExpression: string): string {
  if (fullExpression.startsWith('device.rpcs.')) {
    fullExpression = fullExpression.replace('device.rpcs.', '');
  }
  const args = ((window as any).device as Device).getMethodArguments(
    fullExpression,
  );
  if (args) {
    return `(${args.join(', ')})`;
  }
  return '';
}
