import type {
  Completion,
  CompletionResult,
  CompletionSource,
} from '@codemirror/autocomplete';
import { syntaxTree } from '@codemirror/language';
import type { ParameterCandidate } from './paramToken';

export type CodeEngine = 'javascript' | 'python';

export interface BusinessCompletionConfig {
  engine: CodeEngine;
  paramCandidates: ParameterCandidate[];
}

interface CompletionContextHint {
  from: number;
  validFor: RegExp;
}

const BLOCKED_NODE_NAMES = new Set([
  'String',
  'TemplateString',
  'FormatString',
  'Comment',
  'LineComment',
  'BlockComment',
  'PropertyName',
  'PrivatePropertyName',
]);

const JS_SNIPPETS: Array<{ label: string; insert: string; detail: string }> = [
  {
    label: 'if-else',
    detail: '条件分支',
    insert:
      'if (condition) {\n  _result.value = value;\n} else {\n  _result.value = null;\n}',
  },
  {
    label: 'for-of',
    detail: '遍历数组',
    insert: 'for (const item of items) {\n  // TODO\n}\n_result.done = true;',
  },
  {
    label: 'result-object',
    detail: '返回对象',
    insert: "_result.message = 'ok';\n_result.data = data;",
  },
];

const PYTHON_SNIPPETS: Array<{
  label: string;
  insert: string;
  detail: string;
}> = [
  {
    label: 'if-else',
    detail: '条件分支',
    insert:
      "if condition:\n    _result['value'] = value\nelse:\n    _result['value'] = None",
  },
  {
    label: 'for-loop',
    detail: '遍历数组',
    insert: "for item in items:\n    # TODO\n    pass\n_result['done'] = True",
  },
  {
    label: 'result-object',
    detail: '返回对象',
    insert: "_result['message'] = 'ok'\n_result['data'] = data",
  },
];

export function normalizeCodeEngine(rawEngine?: string): CodeEngine {
  const normalized = (rawEngine || 'js').trim().toLowerCase();
  if (normalized === 'python' || normalized === 'py') {
    return 'python';
  }
  return 'javascript';
}

export function shouldSkipBusinessCompletion(nodeName: string): boolean {
  if (!nodeName) {
    return false;
  }
  if (BLOCKED_NODE_NAMES.has(nodeName)) {
    return true;
  }
  if (nodeName.endsWith('Comment')) {
    return true;
  }
  return nodeName.includes('String');
}

export function createBusinessCompletions(
  targetEngine: CodeEngine,
  candidates: ParameterCandidate[],
): Completion[] {
  const resultCompletion: Completion = {
    label: '_result',
    type: 'variable',
    detail: '代码节点输出对象',
    boost: 900,
  };

  const parameterCompletions: Completion[] = candidates.map((candidate) => ({
    label: candidate.name,
    type: 'variable',
    detail: candidate.resolved ? '参数模板' : '参数模板（未映射）',
    apply: `{{${candidate.name}}}`,
    boost: 700,
  }));

  const snippetCompletions: Completion[] = (
    targetEngine === 'python' ? PYTHON_SNIPPETS : JS_SNIPPETS
  ).map((snippet) => ({
    label: snippet.label,
    type: 'snippet',
    detail: snippet.detail,
    apply: snippet.insert,
    boost: 500,
  }));

  return dedupeCompletions([
    resultCompletion,
    ...parameterCompletions,
    ...snippetCompletions,
  ]);
}

function dedupeCompletions(items: Completion[]): Completion[] {
  const seen = new Set<string>();
  const deduped: Completion[] = [];
  for (const item of items) {
    const key = `${item.label}::${item.type || ''}`;
    if (seen.has(key)) {
      continue;
    }
    seen.add(key);
    deduped.push(item);
  }
  return deduped;
}

function resolveTemplateContext(
  source: string,
  pos: number,
): CompletionContextHint | null {
  const start = Math.max(0, pos - 300);
  const prefix = source.slice(start, pos);
  const match = /\{\{\s*([\w.]*)$/.exec(prefix);
  if (!match) {
    return null;
  }

  return {
    from: pos - match[0].length,
    validFor: /^[\w.]*$/,
  };
}

function resolveWordContext(
  source: string,
  pos: number,
): CompletionContextHint | null {
  let from = pos;
  while (from > 0 && /[\w$]/.test(source[from - 1])) {
    from -= 1;
  }
  if (from === pos) {
    return null;
  }

  if (from > 0 && source[from - 1] === '.') {
    return null;
  }

  return {
    from,
    validFor: /^[\w$]*$/,
  };
}

export function resolveBusinessCompletionContext(
  source: string,
  pos: number,
): CompletionContextHint | null {
  return resolveTemplateContext(source, pos) || resolveWordContext(source, pos);
}

export function createBusinessCompletionSource(
  config: BusinessCompletionConfig,
): CompletionSource {
  return (context) => {
    const node = syntaxTree(context.state).resolveInner(context.pos, -1);
    if (shouldSkipBusinessCompletion(node.name)) {
      return null;
    }

    const docText = context.state.doc.toString();
    const resolved = resolveBusinessCompletionContext(docText, context.pos);
    if (!resolved && !context.explicit) {
      return null;
    }

    const result: CompletionResult = {
      from: resolved?.from ?? context.pos,
      options: createBusinessCompletions(config.engine, config.paramCandidates),
    };

    if (resolved?.validFor) {
      result.validFor = resolved.validFor;
    }

    return result;
  };
}

export function shouldAutoAppendCallParens(
  completion: Completion | null | undefined,
  nextChar: string,
): boolean {
  if (!completion) {
    return false;
  }

  const completionType = (completion.type || '').toLowerCase();
  if (completionType !== 'function' && completionType !== 'method') {
    return false;
  }

  if (nextChar === '(') {
    return false;
  }

  if (typeof completion.apply === 'string' && completion.apply.includes('(')) {
    return false;
  }

  if (typeof completion.apply === 'function') {
    return false;
  }

  return true;
}
