// Copyright (C) 2024 Quickwit, Inc.
//
// Quickwit is offered under the AGPL v3.0 and as commercial software.
// For commercial licensing, contact us at hello@quickwit.io.
//
// AGPL:
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

import { getAllFields, IndexMetadata } from "../../utils/models";

export enum CompletionItemKind {
  Field = 3,
  Operator = 11,
}

const BRACES: [string, string] = ['{', '}'];
const BRACKETS: [string, string] = ['[', ']'];
const PARENTHESES: [string, string] = ['(', ')'];

export const LANGUAGE_CONFIG = {
  comments: {
    lineComment: "//",
  },
  brackets: [ BRACES, BRACKETS, PARENTHESES ],
  autoClosingPairs: [
    { open: "{", close: "}" },
    { open: "[", close: "]" },
    { open: "(", close: ")" },
    { open: '"', close: '"' },
    { open: "'", close: "'" },
  ],
  surroundingPairs: [
    { open: "{", close: "}" },
    { open: "[", close: "]" },
    { open: "(", close: ")" },
    { open: '"', close: '"' },
    { open: "'", close: "'" },
  ],
};

// TODO: clean language features as I (fmassot) did not dig into it yet.
/* eslint-disable  @typescript-eslint/no-explicit-any */
export function LanguageFeatures(): any {
  return {
    defaultToken: "invalid",
    //wordDefinition: /(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,
    operators: ['+', '-'],
    brackets: [
      { open: "(", close: ")", token: "delimiter.parenthesis" },
    ],
    keywords: [
      'AND', 'OR',
    ],
    symbols:  /[=><!~?:&|+\-*/^%]+/,
    escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
    tokenizer: {
      root: [
        // identifiers and keywords
        [/[a-z_$][\w$]*/, { cases: {
          '@keywords': 'keyword',
          '@default': 'identifier' } }],
        [/[A-Z][\w$]*/, 'type.identifier' ],  // to show class names nicely

        // whitespace
        { include: '@whitespace' },

        // delimiters and operators
        [/[{}()[]]/, '@brackets'],
        [/[<>](?!@symbols)/, '@brackets'],
        [/@symbols/, { cases: { '@operators': 'operator',
        '@default'  : '' } } ],

        // @ annotations.
        // As an example, we emit a debugging log message on these tokens.
        // Note: message are suppressed during the first load -- change some lines to see them.
        [/@\s*[a-zA-Z_$][\w$]*/, { token: 'annotation', log: 'annotation token: $0' }],

        // numbers
        [/\d*\.\d+([eE][-+]?\d+)?/, 'number.float'],
        [/0[xX][0-9a-fA-F]+/, 'number.hex'],
        [/\d+/, 'number'],

        // delimiter: after number because of .\d floats
        [/[;,.]/, 'delimiter'],

        // strings
        [/"([^"\\]|\\.)*$/, 'string.invalid' ],  // non-terminated string
        [/"/,  { token: 'string.quote', bracket: '@open', next: '@string' } ],

        // characters
        [/'[^\\']'/, 'string'],
        [/(')(@escapes)(')/, ['string','string.escape','string']],
        [/'/, 'string.invalid']
      ],
      comment: [
        [/[^/*]+/, 'comment' ],
        [/\/\*/,    'comment', '@push' ],    // nested comment
        ["\\*/",    'comment', '@pop'  ],
        [/[/*]/,   'comment' ]
      ],
      string: [
        [/[^\\"]+/,  'string'],
        [/@escapes/, 'string.escape'],
        [/\\./,      'string.escape.invalid'],
        [/"/,        { token: 'string.quote', bracket: '@close', next: '@pop' } ]
      ],

      whitespace: [
        [/[ \t\r\n]+/, 'white'],
        [/\/\*/,       'comment', '@comment' ],
        [/\/\/.*$/,    'comment'],
      ],
    },
  };
}

export const createIndexCompletionProvider = (indexMetadata: IndexMetadata) => {
  const fields = getAllFields(indexMetadata.index_config.doc_mapping.field_mappings);
  const completionProvider = {
    provideCompletionItems(model: any, position: any) {
      const word = model.getWordUntilPosition(position)

      const range = {
        startLineNumber: position.lineNumber,
        endLineNumber: position.lineNumber,
        startColumn: word.startColumn,
        endColumn: word.endColumn,
      }

      // We want to auto complete all fields except timestamp that is handled with `TimeRangeSelect` component.
      const fieldSuggestions = fields
        .filter(field => field.json_path !== indexMetadata.index_config.doc_mapping.timestamp_field)
        .map(field => {
          return {
            label: field.json_path,
            kind: CompletionItemKind.Field,
            insertText: field.field_mapping.type == 'json' ? field.json_path + '.' : field.json_path + ':',
            range: range,
          }
        });

      return {
        suggestions: fieldSuggestions.concat([
          {
            label: 'OR',
            kind: CompletionItemKind.Operator,
            insertText: 'OR ',
            range: range,
          },
          {
            label: 'AND',
            kind: CompletionItemKind.Operator,
            insertText: 'AND ',
            range: range,
          }
        ]),
      }
    },
  }

  return completionProvider
}


export const setErrorMarker = (
  monaco: any,
  editor: any,
  startlineNumber: number,
  startColumnNumber: number,
  message: string,
) => {
  const model = editor.getModel()

  if (model) {
    monaco.editor.setModelMarkers(model, "QuestDBLanguageName", [
      {
        message,
        severity: monaco.MarkerSeverity.Error,
        startLineNumber: startlineNumber,
        endLineNumber: startlineNumber,
        startColumn: startColumnNumber,
        endColumn: startColumnNumber,
      },
    ])
  }
}
