import { describe, expect, it } from 'vitest';
import {
  parseQuery,
  matchQueryPattern,
  generateQuerySuggestions,
  validateQuery,
  QUERY_PATTERNS,
} from './query-parser.js';

// ============================================================================
// parseQuery — intent classification
// ============================================================================

describe('parseQuery — intent classification', () => {
  it('classifies "why" questions as root_cause', () => {
    const result = parseQuery('Why did iOS crash yesterday?');
    expect(result.intent).toBe('root_cause');
  });

  it('classifies "what caused" as root_cause', () => {
    const result = parseQuery('What caused the authentication failures?');
    expect(result.intent).toBe('root_cause');
  });

  it('classifies "show me" as pattern_search', () => {
    const result = parseQuery('Show me similar database errors');
    expect(result.intent).toBe('pattern_search');
  });

  it('classifies "find" as pattern_search', () => {
    const result = parseQuery('Find crashes like the login timeout');
    expect(result.intent).toBe('pattern_search');
  });

  it('classifies "compare" as comparison', () => {
    const result = parseQuery('Compare this week to last week');
    expect(result.intent).toBe('comparison');
  });

  it('classifies "trend" as trend', () => {
    const result = parseQuery('Show the trend for timeout errors over time');
    expect(result.intent).toBe('trend');
  });

  it('classifies "how many" as impact', () => {
    const result = parseQuery('How many users were affected by the crash?');
    expect(result.intent).toBe('impact');
  });

  it('defaults to root_cause for ambiguous queries', () => {
    const result = parseQuery('errors happening on iOS');
    expect(result.intent).toBe('root_cause');
  });
});

// ============================================================================
// parseQuery — entity extraction
// ============================================================================

describe('parseQuery — entity extraction', () => {
  it('extracts iOS platform', () => {
    const result = parseQuery('Show iOS crashes');
    expect(result.entities.platforms).toContain('ios');
  });

  it('extracts Android platform', () => {
    const result = parseQuery('Android login failures');
    expect(result.entities.platforms).toContain('android');
  });

  it('extracts web platform', () => {
    const result = parseQuery('Web dashboard timeout');
    expect(result.entities.platforms).toContain('web');
  });

  it('extracts desktop platform', () => {
    const result = parseQuery('Desktop app crashes on Mac');
    expect(result.entities.platforms).toContain('desktop');
  });

  it('extracts multiple platforms', () => {
    const result = parseQuery('Compare iOS and Android crashes');
    expect(result.entities.platforms).toContain('ios');
    expect(result.entities.platforms).toContain('android');
  });

  it('extracts known product IDs', () => {
    const result = parseQuery('Show errors for lysnrai');
    expect(result.entities.products).toContain('lysnrai');
  });

  it('extracts error types like crash', () => {
    const result = parseQuery('Show me recent crash reports');
    expect(result.entities.errorTypes!.length).toBeGreaterThan(0);
  });

  it('extracts error types like timeout', () => {
    const result = parseQuery('Why are there timeout errors?');
    expect(result.entities.errorTypes!.length).toBeGreaterThan(0);
  });

  it('extracts error types like memory leak', () => {
    const result = parseQuery('Investigate the memory leak');
    expect(result.entities.errorTypes!.length).toBeGreaterThan(0);
  });
});

// ============================================================================
// parseQuery — time range extraction
// ============================================================================

describe('parseQuery — time range extraction', () => {
  it('extracts "yesterday" time range', () => {
    const result = parseQuery('What crashed yesterday?');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const end = new Date(result.entities.timeRange!.end);
    // Start is yesterday midnight, end is yesterday 23:59:59 — span is ~24h
    // But implementation sets start to yesterday-midnight and end to today-23:59, so up to ~48h
    expect(end.getTime() - start.getTime()).toBeLessThanOrEqual(48 * 60 * 60 * 1000);
    expect(start.getHours()).toBe(0);
  });

  it('extracts "last week" time range', () => {
    const result = parseQuery('Show errors from last week');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const now = Date.now();
    const diffDays = (now - start.getTime()) / (24 * 60 * 60 * 1000);
    expect(diffDays).toBeCloseTo(7, 0);
  });

  it('extracts "last month" time range', () => {
    const result = parseQuery('Crashes last month');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const now = Date.now();
    const diffDays = (now - start.getTime()) / (24 * 60 * 60 * 1000);
    expect(diffDays).toBeCloseTo(30, 1);
  });

  it('extracts "N days ago" time range', () => {
    const result = parseQuery('Errors from 3 days ago');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const now = Date.now();
    const diffDays = (now - start.getTime()) / (24 * 60 * 60 * 1000);
    expect(diffDays).toBeCloseTo(3, 0);
  });

  it('extracts "N hours ago" time range', () => {
    const result = parseQuery('What happened 6 hours ago');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const now = Date.now();
    const diffHours = (now - start.getTime()) / (60 * 60 * 1000);
    expect(diffHours).toBeCloseTo(6, 0);
  });

  it('defaults to 7 days when no time specified', () => {
    const result = parseQuery('Show all TypeError errors');
    expect(result.entities.timeRange).toBeDefined();
    const start = new Date(result.entities.timeRange!.start);
    const now = Date.now();
    const diffDays = (now - start.getTime()) / (24 * 60 * 60 * 1000);
    expect(diffDays).toBeCloseTo(7, 0);
  });
});

// ============================================================================
// parseQuery — constraints
// ============================================================================

describe('parseQuery — constraints', () => {
  it('extracts exclusion constraints', () => {
    const result = parseQuery('Show errors excluding android');
    expect(result.constraints).toContain('exclude:android');
  });

  it('extracts beta-only constraint', () => {
    const result = parseQuery('Show only beta crashes');
    expect(result.constraints).toContain('userSegment:beta');
  });

  it('extracts production-only constraint', () => {
    const result = parseQuery('Show only production errors');
    expect(result.constraints).toContain('environment:production');
  });

  it('returns empty constraints for unconstrained queries', () => {
    const result = parseQuery('Why did the app crash?');
    expect(result.constraints).toHaveLength(0);
  });
});

// ============================================================================
// matchQueryPattern
// ============================================================================

describe('matchQueryPattern', () => {
  it('matches root_cause queries to root_cause_investigation pattern', () => {
    const parsed = parseQuery('Why did the authentication fail?');
    const pattern = matchQueryPattern(parsed);
    expect(pattern).toBeDefined();
    expect(pattern!.name).toBe('root_cause_investigation');
  });

  it('matches pattern_search queries', () => {
    const parsed = parseQuery('Find similar timeout errors');
    const pattern = matchQueryPattern(parsed);
    expect(pattern).toBeDefined();
    expect(pattern!.name).toBe('similar_errors_search');
  });

  it('matches impact queries', () => {
    const parsed = parseQuery('How many users were affected?');
    const pattern = matchQueryPattern(parsed);
    expect(pattern).toBeDefined();
    expect(pattern!.name).toBe('impact_assessment');
  });

  it('matches comparison queries', () => {
    const parsed = parseQuery('Compare these two error clusters');
    const pattern = matchQueryPattern(parsed);
    expect(pattern).toBeDefined();
    expect(pattern!.name).toBe('cluster_comparison');
  });
});

// ============================================================================
// generateQuerySuggestions
// ============================================================================

describe('generateQuerySuggestions', () => {
  it('generates suggestions based on error types', () => {
    const suggestions = generateQuerySuggestions(['TypeError', 'NetworkError'], ['ios']);
    expect(suggestions.length).toBeGreaterThan(0);
    expect(suggestions.some(s => s.query.includes('TypeError'))).toBe(true);
  });

  it('includes platform-specific suggestions', () => {
    const suggestions = generateQuerySuggestions(['CrashError'], ['android', 'web']);
    expect(suggestions.some(s => s.query.includes('android'))).toBe(true);
  });

  it('always includes impact and trend suggestions', () => {
    const suggestions = generateQuerySuggestions([], []);
    expect(suggestions.some(s => s.pattern === 'impact_assessment')).toBe(true);
    expect(suggestions.some(s => s.pattern === 'trend_analysis')).toBe(true);
  });

  it('limits suggestions to 6', () => {
    const suggestions = generateQuerySuggestions(
      ['A', 'B', 'C', 'D', 'E'],
      ['ios', 'android', 'web']
    );
    expect(suggestions.length).toBeLessThanOrEqual(6);
  });
});

// ============================================================================
// validateQuery
// ============================================================================

describe('validateQuery', () => {
  it('returns valid for specific queries', () => {
    const parsed = parseQuery('Why did iOS crash yesterday?');
    const result = validateQuery(parsed);
    expect(result.valid).toBe(true);
    expect(result.errors).toHaveLength(0);
  });

  it('warns when query is too broad', () => {
    const parsed = parseQuery('What happened?');
    const result = validateQuery(parsed);
    expect(result.warnings.length).toBeGreaterThan(0);
    expect(result.warnings[0]).toContain('broad');
  });

  it('warns when time range exceeds 90 days', () => {
    const parsed = parseQuery('Show errors');
    // Override time range to > 90 days
    parsed.entities.timeRange = {
      start: new Date(Date.now() - 120 * 86400000).toISOString(),
      end: new Date().toISOString(),
    };
    const result = validateQuery(parsed);
    expect(result.warnings.some(w => w.includes('90 days'))).toBe(true);
  });
});

// ============================================================================
// QUERY_PATTERNS constant
// ============================================================================

describe('QUERY_PATTERNS', () => {
  it('has at least 5 patterns', () => {
    expect(QUERY_PATTERNS.length).toBeGreaterThanOrEqual(5);
  });

  it('each pattern has required fields', () => {
    for (const pattern of QUERY_PATTERNS) {
      expect(pattern.name).toBeTruthy();
      expect(pattern.description).toBeTruthy();
      expect(pattern.examples.length).toBeGreaterThan(0);
      expect(pattern.intent).toBeTruthy();
    }
  });
});
