'use strict';

const VALID_TYPES = new Set([
  'string', 'text', 'integer', 'bigint', 'decimal',
  'boolean', 'date', 'timestamp', 'uuid', 'json'
]);

const STANDALONE_CONSTRAINTS = new Set(['pk', 'notnull', 'unique', 'index']);
const VALUE_CONSTRAINTS = new Set(['default', 'fk']);

const IDENTIFIER_RE = /^[a-z_][a-z0-9_]*$/i;
const POSITIVE_INT_RE = /^[1-9]\d*$/;
const NON_NEGATIVE_INT_RE = /^\d+$/;
const SIGNED_INT_RE = /^-?\d+$/;

function parseFieldShorthand(shorthand, fieldName) {
  if (typeof shorthand !== 'string' || shorthand.trim() === '') {
    throw new Error(`Field '${fieldName}': shorthand must be a non-empty string`);
  }

  const tokens = tokenize(shorthand.trim(), fieldName);
  const result = parseTypeToken(tokens[0], fieldName);

  const seen = new Set();
  for (let i = 1; i < tokens.length; i++) {
    parseConstraintToken(tokens[i], result, seen, fieldName);
  }

  return result;
}

function tokenize(input, fieldName) {
  const tokens = [];
  let current = '';
  let inQuote = false;

  for (let i = 0; i < input.length; i++) {
    const ch = input[i];

    if (ch === '"') {
      throw new Error(`Field '${fieldName}': double quote not supported in shorthand, use single quote for string literals`);
    }

    if (ch === "'") {
      inQuote = !inQuote;
      current += ch;
      continue;
    }

    if (!inQuote && /\s/.test(ch)) {
      if (current !== '') {
        tokens.push(current);
        current = '';
      }
      continue;
    }

    current += ch;
  }

  if (inQuote) {
    throw new Error(`Field '${fieldName}': unterminated string literal in default value`);
  }

  if (current !== '') {
    tokens.push(current);
  }

  return tokens;
}

function parseTypeToken(token, fieldName) {
  const colonIdx = token.indexOf(':');
  const type = colonIdx === -1 ? token : token.slice(0, colonIdx);
  const modifier = colonIdx === -1 ? null : token.slice(colonIdx + 1);

  if (!VALID_TYPES.has(type)) {
    throw new Error(`Field '${fieldName}': unknown type '${type}'`);
  }

  const result = { type };

  if (type === 'string') {
    if (modifier === null || modifier === '') {
      throw new Error(`Field '${fieldName}': type 'string' requires length modifier (e.g., string:20)`);
    }
    if (!POSITIVE_INT_RE.test(modifier)) {
      throw new Error(`Field '${fieldName}': string length must be a positive integer, got '${modifier}'`);
    }
    result.length = Number(modifier);
    return result;
  }

  if (type === 'decimal') {
    if (modifier === null || modifier === '') {
      throw new Error(`Field '${fieldName}': type 'decimal' requires precision and scale (e.g., decimal:15,2)`);
    }
    const parts = modifier.split(',');
    if (parts.length !== 2 || !POSITIVE_INT_RE.test(parts[0]) || !NON_NEGATIVE_INT_RE.test(parts[1])) {
      throw new Error(`Field '${fieldName}': decimal modifier must be 'precision,scale' with positive integers, got '${modifier}'`);
    }
    const precision = Number(parts[0]);
    const scale = Number(parts[1]);
    if (scale > precision) {
      throw new Error(`Field '${fieldName}': decimal scale (${scale}) cannot exceed precision (${precision})`);
    }
    result.precision = precision;
    result.scale = scale;
    return result;
  }

  if (modifier !== null) {
    throw new Error(`Field '${fieldName}': type '${type}' does not accept modifier`);
  }

  return result;
}

function parseConstraintToken(token, result, seen, fieldName) {
  const colonIdx = token.indexOf(':');
  const name = colonIdx === -1 ? token : token.slice(0, colonIdx);
  const rawValue = colonIdx === -1 ? null : token.slice(colonIdx + 1);

  if (STANDALONE_CONSTRAINTS.has(name)) {
    if (rawValue !== null) {
      throw new Error(`Field '${fieldName}': constraint '${name}' does not accept a value`);
    }
    if (seen.has(name)) {
      throw new Error(`Field '${fieldName}': duplicate constraint '${name}'`);
    }
    seen.add(name);
    result[name] = true;
    return;
  }

  if (VALUE_CONSTRAINTS.has(name)) {
    if (rawValue === null || rawValue === '') {
      throw new Error(`Field '${fieldName}': constraint '${name}' requires a value (use '${name}:VALUE')`);
    }
    if (seen.has(name)) {
      throw new Error(`Field '${fieldName}': duplicate constraint '${name}'`);
    }
    seen.add(name);

    if (name === 'default') {
      result.default = parseDefaultValue(rawValue, fieldName);
    } else if (name === 'fk') {
      result.fk = parseFkValue(rawValue, fieldName);
    }
    return;
  }

  throw new Error(`Field '${fieldName}': unknown constraint '${name}'`);
}

function parseDefaultValue(value, fieldName) {
  if (value === 'true') return { kind: 'literal', value: true };
  if (value === 'false') return { kind: 'literal', value: false };

  if (SIGNED_INT_RE.test(value)) {
    return { kind: 'literal', value: Number(value) };
  }

  if (value.startsWith("'")) {
    if (value.length < 2 || !value.endsWith("'")) {
      throw new Error(`Field '${fieldName}': unterminated string literal in default value`);
    }
    return { kind: 'literal', value: value.slice(1, -1) };
  }

  if (value.endsWith('()')) {
    const fname = value.slice(0, -2);
    if (!IDENTIFIER_RE.test(fname)) {
      throw new Error(`Field '${fieldName}': invalid default value '${value}'`);
    }
    return { kind: 'function', name: fname, args: [] };
  }

  if (IDENTIFIER_RE.test(value)) {
    return { kind: 'constant', value };
  }

  throw new Error(`Field '${fieldName}': invalid default value '${value}'`);
}

function parseFkValue(value, fieldName) {
  const dotIdx = value.indexOf('.');
  if (dotIdx === -1) {
    throw new Error(`Field '${fieldName}': invalid foreign key format '${value}' (expected 'table.column')`);
  }

  const table = value.slice(0, dotIdx);
  const column = value.slice(dotIdx + 1);

  if (!IDENTIFIER_RE.test(table) || !IDENTIFIER_RE.test(column)) {
    throw new Error(`Field '${fieldName}': invalid foreign key format '${value}' (expected 'table.column')`);
  }

  return { table, column };
}

module.exports = { parseFieldShorthand };
