/**
 * Tree-sitter grammar for Storybook DSL
 *
 * This grammar defines the syntax for the Storybook narrative DSL,
 * including characters, templates, relationships, life arcs, behaviors, etc.
 */

module.exports = grammar({
  name: 'storybook',

  // externals: $ => [
  //   $._prose_block_content,
  //   $._prose_block_end
  // ],

  extras: $ => [
    /\s/,  // Whitespace
    $.line_comment,
    $.block_comment
  ],

  conflicts: $ => [
    [$.path_segments],
    [$.sub_concept_enum_body, $.sub_concept_record_body],
    [$.uses_behaviors]
  ],

  word: $ => $.identifier,

  rules: {
    // Top-level structure
    source_file: $ => repeat($.declaration),

    // Comments
    line_comment: $ => token(seq('//', /.*/)),
    block_comment: $ => token(seq('/*', /[^*]*\*+([^/*][^*]*\*+)*/, '/')),

    // Declarations
    declaration: $ => choice(
      $.use_declaration,
      $.character_declaration,
      $.template_declaration,
      $.life_arc_declaration,
      $.schedule_declaration,
      $.behavior_declaration,
      $.institution_declaration,
      $.relationship_declaration,
      $.location_declaration,
      $.species_declaration,
      $.enum_declaration,
      $.concept_declaration,
      $.sub_concept,
      $.definition
    ),

    // Use declarations
    use_declaration: $ => seq(
      'use',
      $.path_segments,
      optional(choice(
        seq('::', '{', commaSep1($.identifier), '}'),
        seq('::', '*')
      )),
      ';'
    ),

    path: $ => $.path_segments,

    path_segments: $ => sep1($.identifier, token('::')),

    // Character declaration
    character_declaration: $ => seq(
      'character',
      field('name', $.identifier),
      optional(seq(':', field('species', $.identifier))),
      optional(field('template', $.template_clause)),
      field('body', $.character_body)
    ),

    character_body: $ => seq(
      '{',
      repeat($.field),
      '}'
    ),

    template_clause: $ => seq('from', commaSep1($.identifier)),

    // Template declaration
    template_declaration: $ => seq(
      'template',
      field('name', $.identifier),
      optional(seq(':', field('species', $.identifier))),
      optional('strict'),
      field('body', $.template_body)
    ),

    template_body: $ => seq(
      '{',
      repeat(choice(
        $.include,
        $.uses_behaviors,
        $.uses_schedule,
        $.field
      )),
      '}'
    ),

    uses_behaviors: $ => seq(
      'uses',
      'behaviors',
      ':',
      commaSep1($.identifier)
    ),

    uses_schedule: $ => seq(
      'uses',
      choice('schedule', 'schedules'),
      ':',
      choice(
        $.identifier,
        seq('[', commaSep1($.identifier), ']')
      )
    ),

    include: $ => seq('include', $.identifier),

    // Fields (key: value pairs)
    field: $ => choice(
      seq(
        field('name', $.dotted_path),
        ':',
        field('value', $.value)
      ),
      $.prose_block
    ),

    dotted_path: $ => sep1($.identifier, '.'),

    // Values
    value: $ => choice(
      $.integer,
      $.float,
      $.string,
      $.boolean,
      $.range,
      $.time,
      $.duration,
      $.path,
      $.prose_block,
      $.list,
      $.object,
      $.override
    ),

    integer: $ => /-?[0-9]+/,

    float: $ => /-?[0-9]+\.[0-9]+/,

    string: $ => /"([^"\\]|\\.)*"/,

    boolean: $ => choice('true', 'false'),

    range: $ => choice(
      seq($.integer, '..', $.integer),
      seq($.float, '..', $.float)
    ),

    time: $ => /[0-9]{2}:[0-9]{2}(:[0-9]{2})?/,

    duration: $ => /[0-9]+[dhms]([0-9]+[dhms])*/,

    list: $ => seq('[', commaSep($.value), ']'),

    object: $ => $.block,

    block: $ => seq('{', repeat($.field), '}'),

    // Override (@base { remove field, field: value })
    override: $ => seq(
      '@',
      $.path,
      '{',
      repeat($.override_op),
      '}'
    ),

    override_op: $ => choice(
      seq('remove', $.identifier),
      seq('append', $.field),
      $.field
    ),

    // Prose blocks (---tag content ---)
    prose_block: $ => seq(
      field('marker', $.prose_marker),
      field('tag', $.identifier),
      optional(/[^\n]*/), // Rest of opening line
      field('content', $.prose_content),
      field('end', $.prose_marker)
    ),

    prose_marker: $ => '---',

    // Capture prose content as a single token for markdown injection
    prose_content: $ => token(prec(-1, repeat1(choice(
      /[^\-]+/,  // Any non-dash characters
      /-[^\-]/,  // Single dash not followed by another dash
      /-\-[^\-]/,  // Two dashes not followed by another dash
    )))),

    // Life arc declaration
    life_arc_declaration: $ => seq(
      'life_arc',
      field('name', $.identifier),
      optional(field('requires', $.requires_clause)),
      '{',
      repeat($.field),
      repeat($.state_block),
      '}'
    ),

    requires_clause: $ => seq(
      'requires',
      '{',
      commaSep1($.required_field),
      '}'
    ),

    required_field: $ => seq(
      field('name', $.identifier),
      ':',
      field('type', $.identifier)
    ),

    state_block: $ => seq(
      'state',
      field('name', $.identifier),
      field('body', $.state_body)
    ),

    state_body: $ => seq(
      '{',
      optional($.on_enter),
      repeat($.field),
      repeat($.transition),
      '}'
    ),

    on_enter: $ => seq('on', 'enter', $.block),

    transition: $ => seq(
      'on',
      field('condition', $.expression),
      '->',
      field('target', $.identifier)
    ),

    // Schedule declaration
    schedule_declaration: $ => seq(
      'schedule',
      field('name', $.identifier),
      optional(seq('modifies', field('modifies', $.identifier))),
      field('body', $.schedule_body)
    ),

    schedule_body: $ => seq(
      '{',
      repeat(choice(
        $.field,
        $.schedule_block,
        $.override_block,
        $.recurrence_block
      )),
      '}'
    ),

    // Named block: block name { time range, action, fields }
    schedule_block: $ => seq(
      'block',
      field('name', $.identifier),
      '{',
      field('time_range', $.time_range),
      repeat($.block_field),
      '}'
    ),

    // Override block: override name { time range, action, fields }
    override_block: $ => seq(
      'override',
      field('name', $.identifier),
      '{',
      field('time_range', $.time_range),
      repeat($.block_field),
      '}'
    ),

    // Recurrence: recurrence Name on DayOfWeek { blocks }
    recurrence_block: $ => seq(
      'recurrence',
      field('name', $.identifier),
      'on',
      field('day', $.identifier),
      '{',
      repeat1($.schedule_block),
      '}'
    ),

    time_range: $ => seq(
      field('start', $.time),
      '->',
      field('end', $.time),
      optional(',')
    ),

    block_field: $ => seq(
      field('name', $.identifier),
      ':',
      field('value', choice($.identifier, $.string, $.integer, $.float))
    ),

    // Behavior tree declaration
    behavior_declaration: $ => seq(
      'behavior',
      field('name', $.identifier),
      field('body', $.behavior_body)
    ),

    behavior_body: $ => seq(
      '{',
      repeat($.field),
      field('root', $.behavior_node),
      '}'
    ),

    behavior_node: $ => choice(
      $.selector_node,
      $.sequence_node,
      $.condition_node,
      $.if_decorator_node,
      $.repeat_node,
      $.decorator_node,
      $.action_node,
      $.subtree_node
    ),

    // Selector node: choose { ... }
    selector_node: $ => seq(
      'choose',
      optional(field('label', $.identifier)),
      '{',
      repeat1($.behavior_node),
      '}'
    ),

    // Sequence node: then { ... }
    sequence_node: $ => seq(
      'then',
      optional(field('label', $.identifier)),
      '{',
      repeat1($.behavior_node),
      '}'
    ),

    // Condition node: if(expr) or when(expr) - NO BRACES
    condition_node: $ => seq(
      choice('if', 'when'),
      '(',
      field('condition', $.expression),
      ')'
    ),

    // If decorator: if(expr) { child } - WITH BRACES
    if_decorator_node: $ => seq(
      'if',
      '(',
      field('condition', $.expression),
      ')',
      '{',
      field('child', $.behavior_node),
      '}'
    ),

    // Repeat node: repeat { child } or repeat(N) { child } or repeat(min..max) { child }
    repeat_node: $ => seq(
      'repeat',
      optional(field('params', choice(
        seq('(', $.integer, ')'),
        seq('(', $.integer, '..', $.integer, ')'),
        seq('(', $.duration, ')')
      ))),
      '{',
      field('child', $.behavior_node),
      '}'
    ),

    // Decorator node: retry/timeout/etc { child }
    decorator_node: $ => seq(
      field('decorator', $.decorator_keyword),
      optional(field('params', $.decorator_params)),
      '{',
      field('child', $.behavior_node),
      '}'
    ),

    decorator_keyword: $ => choice(
      'invert',
      'retry',
      'timeout',
      'cooldown',
      'succeed_always',
      'fail_always'
    ),

    decorator_params: $ => seq(
      '(',
      choice(
        // min..max range (for repeat)
        seq($.integer, '..', $.integer),
        // N (for repeat, retry)
        $.integer,
        // duration (for timeout, cooldown)
        $.duration
      ),
      ')'
    ),

    // Action node: action_name or action_name(params)
    action_node: $ => choice(
      seq($.identifier, '(', commaSep($.action_param), ')'),
      $.identifier
    ),

    action_param: $ => choice(
      // Named parameter: name: value
      seq($.dotted_path, ':', $.value),
      // Positional parameter: just value
      $.value
    ),

    // Subtree node: include path::to::subtree
    subtree_node: $ => seq('include', $.path),

    // Institution declaration
    institution_declaration: $ => seq(
      'institution',
      field('name', $.identifier),
      field('body', $.block)
    ),

    // Relationship declaration
    relationship_declaration: $ => seq(
      'relationship',
      field('name', $.identifier),
      field('body', $.relationship_body)
    ),

    relationship_body: $ => seq(
      '{',
      repeat1($.participant),
      repeat($.field),
      '}'
    ),

    participant: $ => choice(
      // name as role { fields } (role optional)
      seq($.path, 'as', $.identifier, $.block),
      // name { fields } (block required)
      seq($.path, $.block)
    ),

    // Location declaration
    location_declaration: $ => seq(
      'location',
      field('name', $.identifier),
      field('body', $.block)
    ),

    // Species declaration
    species_declaration: $ => seq(
      'species',
      field('name', $.identifier),
      field('body', $.species_body)
    ),

    species_body: $ => seq(
      '{',
      repeat($.include),
      repeat($.species_field),
      '}'
    ),

    species_field: $ => choice(
      // Field with range: name: min..max
      seq(
        field('name', $.identifier),
        ':',
        field('value', $.range)
      ),
      // Field with single value: name: value
      seq(
        field('name', $.identifier),
        ':',
        field('value', choice($.integer, $.float, $.boolean, $.string))
      ),
      $.prose_block
    ),

    // Enum declaration
    enum_declaration: $ => seq(
      'enum',
      field('name', $.identifier),
      '{',
      commaSep1($.identifier),
      '}'
    ),

    // Concept declaration - base type with no structure
    concept_declaration: $ => seq(
      'concept',
      field('name', $.identifier)
    ),

    // Sub-concept declaration - enum or record subtype with dot notation
    sub_concept: $ => seq(
      'sub_concept',
      field('parent', $.identifier),
      '.',
      field('name', $.identifier),
      '{',
      field('body', choice(
        $.sub_concept_record_body,
        $.sub_concept_enum_body
      )),
      '}'
    ),

    // Enum form: Variant1, Variant2, Variant3
    sub_concept_enum_body: $ => commaSep1($.identifier),

    // Record form: field_name: value, field_name: value
    sub_concept_record_body: $ => commaSep1($.sub_concept_field),

    sub_concept_field: $ => seq(
      field('name', $.identifier),
      ':',
      field('value', choice($.integer, $.float, $.string, $.boolean))
    ),

    any_type: $ => 'any',

    // Definition - compile-time pattern matching
    definition: $ => seq(
      'definition',
      field('name', $.identifier),
      '{',
      commaSep1($.variant_pattern),
      '}'
    ),

    // A named variant with field conditions
    variant_pattern: $ => seq(
      field('name', $.identifier),
      ':',
      '{',
      commaSep1($.field_condition),
      '}'
    ),

    // Field condition: field_name: any OR field_name: Type is Value [or Type is Value]*
    field_condition: $ => seq(
      field('name', $.identifier),
      ':',
      field('condition', choice(
        $.any_type,
        $.is_condition
      ))
    ),

    // Is condition: Type is Value [or Type is Value]*
    is_condition: $ => seq(
      $.is_value,
      repeat(seq('or', $.is_value))
    ),

    is_value: $ => seq(
      field('field', $.identifier),
      'is',
      field('value', $.identifier)
    ),

    // Expressions (for conditions in life arcs)
    expression: $ => choice(
      $.or_expression,
      $.and_expression,
      $.not_expression,
      $.comparison,
      $.field_access,
      $.primary_expression
    ),

    or_expression: $ => prec.left(1, seq(
      $.expression,
      'or',
      $.expression
    )),

    and_expression: $ => prec.left(2, seq(
      $.expression,
      'and',
      $.expression
    )),

    not_expression: $ => prec(3, seq('not', $.expression)),

    comparison: $ => prec.left(4, seq(
      $.expression,
      field('operator', choice('is', '>', '>=', '<', '<=')),
      $.expression
    )),

    field_access: $ => prec.left(5, seq(
      $.expression,
      '.',
      $.identifier
    )),

    primary_expression: $ => choice(
      'self',
      'other',
      $.integer,
      $.float,
      $.string,
      $.boolean,
      $.path
    ),

    // Identifiers
    identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
  }
});

/**
 * Helper to create comma-separated lists with optional trailing comma
 */
function commaSep(rule) {
  return optional(commaSep1(rule));
}

function commaSep1(rule) {
  return seq(rule, repeat(seq(',', rule)), optional(','));
}

/**
 * Helper to create separator-based lists (e.g., :: or .)
 */
function sep1(rule, separator) {
  return seq(rule, repeat(seq(separator, rule)));
}
