import { CompletionItem, CompletionItemKind, InsertTextFormat, InsertTextMode } from 'vscode-languageserver'

import { convertAttributesToCompletionItems, convertToCompletionItems } from './internals'

import * as completions from './completions.json'
import { PreviewFeatures } from '../types'

export const sortLengthProperties: CompletionItem[] = convertToCompletionItems(
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  completions.fieldAttributes
    .find((item) => item.label === '@unique')!
    .params.filter((item) => item.label === 'length' || item.label === 'sort'),
  CompletionItemKind.Property,
)

export const relationArguments: CompletionItem[] = convertAttributesToCompletionItems(
  completions.relationArguments,
  CompletionItemKind.Property,
)

const sqlServerClusteredValuesCompletionItems: CompletionItem[] = [
  {
    label: 'true',
    kind: CompletionItemKind.Value,
    insertTextFormat: InsertTextFormat.PlainText,
    documentation: {
      kind: 'markdown',
      value: 'CLUSTERED',
    },
  },
  {
    label: 'false',
    kind: CompletionItemKind.Value,
    insertTextFormat: InsertTextFormat.PlainText,
    documentation: {
      kind: 'markdown',
      value: 'NONCLUSTERED',
    },
  },
]

/**
 * ```prisma
 * model A {
 *  id    Int @id
 *  field Int @unique(sort: |)
 *  otherField Int
 *
 *  \@@unique(fields: [otherField(sort: |)])
 *  \@@index(fields: [id(sort: |)])
 * }
 * ```
 * And then specifically Sql Server, we also return:
 * ```prisma
 *  model A {
 *  id    Int @id(sort: |)
 *
 *  \@@id(fields: [id(sort: |)])
 * }
 * ```
 */
const sortValuesCompletionItems: CompletionItem[] = [
  {
    label: 'Asc',
    kind: CompletionItemKind.Enum,
    insertTextFormat: InsertTextFormat.PlainText,
    documentation: {
      kind: 'markdown',
      value: 'Ascending',
    },
  },
  {
    label: 'Desc',
    kind: CompletionItemKind.Enum,
    insertTextFormat: InsertTextFormat.PlainText,
    documentation: {
      kind: 'markdown',
      value: 'Descending',
    },
  },
]

const clusteredCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'clustered',
    insertText: 'clustered: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation:
      'An index, unique constraint or primary key can be created as clustered or non-clustered; altering the storage and retrieve behavior of the index.',
  })

const typeIndexCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'type',
    kind: CompletionItemKind.Property,
    insertText: 'type: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    insertTextMode: InsertTextMode.adjustIndentation,
    documentation: {
      kind: 'markdown',
      value: 'Defines the access type of indexes: BTree (default) or Hash.',
    },
  })

export const opsIndexFulltextCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'ops',
    insertText: 'ops: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation: 'Specify the operator class for an indexed field.',
  })

//#region COCKROACHDB ONLY
export const virtualSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'virtual',
    insertText: 'virtual',
    kind: CompletionItemKind.Property,
    documentation:
      'Virtual sequences are sequences that do not generate monotonically increasing values and instead produce values like those generated by the built-in function unique_rowid(). They are intended for use in combination with SERIAL-typed columns.',
  })

export const minValueSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'minValue',
    insertText: 'minValue: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation: 'The new minimum value of the sequence.',
  })

export const maxValueSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'maxValue',
    insertText: 'maxValue: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation: 'The new maximum value of the sequence.',
  })

export const cacheSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'cache',
    insertText: 'cache: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation:
      'The number of sequence values to cache in memory for reuse in the session. A cache size of 1 means that there is no cache, and cache sizes of less than 1 are not valid.',
  })

export const incrementSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'increment',
    insertText: 'increment: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation:
      'The new value by which the sequence is incremented. A negative number creates a descending sequence. A positive number creates an ascending sequence.',
  })

export const startSequenceDefaultCompletion = (items: CompletionItem[]) =>
  items.push({
    label: 'start',
    insertText: 'start: $0',
    insertTextFormat: InsertTextFormat.Snippet,
    kind: CompletionItemKind.Property,
    documentation:
      'The value the sequence starts at if you RESTART or if the sequence hits the MAXVALUE and CYCLE is set.',
  })
//#endregion

export const scalarListDefaultCompletion = (items: CompletionItem[]) =>
  items.unshift({
    label: '[]',
    insertText: '[$0]',
    insertTextFormat: InsertTextFormat.Snippet,
    documentation: 'Set a default value on the list field',
    kind: CompletionItemKind.Value,
  })

export const booleanDefaultCompletions = (items: CompletionItem[]) =>
  items.push({ label: 'true', kind: CompletionItemKind.Value }, { label: 'false', kind: CompletionItemKind.Value })

export function filterSortLengthBasedOnInput(
  attribute: '@@unique' | '@unique' | '@@id' | '@id' | '@@index',
  previewFeatures: PreviewFeatures[] | undefined,
  datasourceProvider: string | undefined,
  wordBeforePosition: string,
  items: CompletionItem[],
): CompletionItem[] {
  /*
   * 1 - Autocomplete values
   */
  // Auto completion for sort: Desc | Asc
  // includes because `@unique(sort: |)` means wordBeforePosition = '@unique(sort:'
  if (wordBeforePosition.includes('sort:')) {
    return sortValuesCompletionItems
  } else {
    /*
     * 2 - Autocomplete properties
     */

    // The length argument is available on MySQL only on the
    // @id, @@id, @unique, @@unique and @@index fields.

    // The sort argument is available for all databases on the
    // @unique, @@unique and @@index fields.
    // Additionally, SQL Server also allows it on @id and @@id.

    // Which translates too
    // - `length` argument for `@id`, `@@id`, `@unique`, `@@unique` and `@@index` (MySQL only)
    // - Note that on the `@@` the argument is on available a field - not on the top level attribute
    // - `sort` argument for `@unique`, `@@unique` and `@@index` (Additionally `@id` and `@@id` for SQL Server)

    if (datasourceProvider === 'mysql') {
      if (['@unique', '@@unique', '@@index'].includes(attribute)) {
        return items
      } else {
        // filter sort out
        return items.filter((arg) => arg.label !== 'sort')
      }
    } else if (datasourceProvider === 'sqlserver') {
      if (['@unique', '@@unique', '@@index', '@id', '@@id'].includes(attribute)) {
        // only filter length out
        return items.filter((arg) => arg.label !== 'length')
      } else {
        // filter length and sort out
        return items.filter((arg) => arg.label !== 'length' && arg.label !== 'sort')
      }
    } else {
      if (['@unique', '@@unique', '@@index'].includes(attribute)) {
        // only filter length out
        return items.filter((arg) => arg.label !== 'length')
      } else {
        // filter length and sort out
        return items.filter((arg) => arg.label !== 'length' && arg.label !== 'sort')
      }
    }
  }
}

export function getCompletionsForFieldAttributeArgs(
  fieldAttributeWithParams: '@unique' | '@id',
  previewFeatures: PreviewFeatures[] | undefined,
  datasourceProvider: string | undefined,
  wordBeforePosition: string,
): CompletionItem[] {
  const items = convertToCompletionItems(
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    completions.fieldAttributes.find((item) => item.label.includes(fieldAttributeWithParams))!.params,
    CompletionItemKind.Property,
  )

  const completionItems = filterSortLengthBasedOnInput(
    fieldAttributeWithParams,
    previewFeatures,
    datasourceProvider,
    wordBeforePosition,
    items,
  )

  if (datasourceProvider === 'sqlserver') {
    // Auto completion for SQL Server only, clustered: true | false
    if (wordBeforePosition.includes('clustered:')) {
      return sqlServerClusteredValuesCompletionItems
    }
    // add clustered propery to completion items
    completionItems.push({
      label: 'clustered',
      insertText: 'clustered: $0',
      insertTextFormat: InsertTextFormat.Snippet,
      kind: CompletionItemKind.Property,
      documentation:
        'An index, unique constraint or primary key can be created as clustered or non-clustered; altering the storage and retrieve behavior of the index.',
    })
  }

  return completionItems
}

export function getCompletionsForBlockAttributeArgs({
  blockAttributeWithParams,
  wordBeforePosition,
  datasourceProvider,
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  previewFeatures,
}: {
  blockAttributeWithParams: '@@unique' | '@@id' | '@@index' | '@@fulltext'
  wordBeforePosition: string
  datasourceProvider: string | undefined
  previewFeatures: PreviewFeatures[] | undefined
}): CompletionItem[] {
  const items = convertToCompletionItems(
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    completions.blockAttributes.find((item) => item.label.includes(blockAttributeWithParams))!.params,
    CompletionItemKind.Property,
  )

  // SQL Server only, suggest clustered
  if (datasourceProvider === 'sqlserver' && blockAttributeWithParams !== '@@fulltext') {
    // Auto completion for SQL Server only, clustered: true | false
    if (wordBeforePosition.includes('clustered:')) {
      return sqlServerClusteredValuesCompletionItems
    } else {
      // add clustered to suggestions
      clusteredCompletion(items)
    }
  }
  // PostgreSQL only, suggest type
  else if (
    blockAttributeWithParams === '@@index' &&
    datasourceProvider &&
    ['postgresql', 'postgres'].includes(datasourceProvider)
  ) {
    // TODO (Joël) figure out if we need to add cockroachdb provider here
    // The type argument is only available for PostgreSQL on @@index
    typeIndexCompletion(items)
  }

  return items
}
