import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from 'vitest';
import express, { Application } from 'express';
import request from 'supertest';
import { setupAgileAPI } from '../../api/agile.js';
import { SQLiteManager } from '../../../storage/sqlite-manager.js';
import { randomUUID } from 'crypto';
import path from 'path';
import fs from 'fs/promises';

// Mock the ensureDatabaseReady function before imports
let testDb: SQLiteManager;

vi.mock('../../../storage/sqlite-manager.js', async () => {
  const actual = await vi.importActual('../../../storage/sqlite-manager.js') as any;
  return {
    ...actual,
    ensureDatabaseReady: () => {
      if (!testDb) {
        throw new Error('Test database not initialized');
      }
      return Promise.resolve(testDb);
    }
  };
});

describe('Agile API Integration Tests', () => {
  let app: Application;
  const testDbPath = '.atlas/test-agile-integration.db';

  beforeAll(async () => {
    // Ensure test directory exists
    await fs.mkdir(path.dirname(testDbPath), { recursive: true });
    
    // Initialize test database
    testDb = new SQLiteManager(testDbPath);
    await testDb.initialize();
    
    // Setup Express app
    app = express();
    app.use(express.json());
    
    setupAgileAPI(app, null);
  });

  afterAll(async () => {
    // Clean up test database
    await testDb.close();
    try {
      await fs.unlink(testDbPath);
    } catch (e) {
      // Ignore if file doesn't exist
    }
  });

  beforeEach(async () => {
    // Clear data in correct order to respect foreign keys
    await testDb.run('DELETE FROM agile_stories');
    await testDb.run('DELETE FROM agile_sprints');
    await testDb.run('DELETE FROM projects');
    
    // Create default project for tests
    await testDb.run(`
      INSERT INTO projects (id, name, description, config) 
      VALUES ('default', 'Default Test Project', 'Project for integration tests', '{}')
    `);
  });

  describe('Sprint Board 404 Regression Test', () => {
    it('should find individual sprints that appear in the list endpoint', async () => {
      const sprintId = 'sprint-' + randomUUID();
      const sprintName = 'Test Sprint for 404 Bug';
      
      // Insert a test sprint directly into the database
      const insertResult = await testDb.run(`
        INSERT INTO agile_sprints (
          id, project_id, name, goal, status, 
          start_date, end_date, duration, team,
          story_points_planned, story_points_completed,
          stories_total, stories_completed, velocity,
          created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `, [
        sprintId,
        'default',
        sprintName,
        'Test goal',
        'active',
        Date.now(),
        Date.now() + 14 * 24 * 60 * 60 * 1000,
        14,
        '["Developer 1", "Developer 2"]',
        20,
        10,
        5,
        2,
        10,
        Date.now(),
        Date.now()
      ]);
      
      expect(insertResult.success).toBe(true);

      // Step 1: Verify the sprint appears in the list endpoint
      const listResponse = await request(app).get('/api/agile/sprints');
      
      expect(listResponse.status).toBe(200);
      expect(listResponse.body.success).toBe(true);
      expect(listResponse.body.data.sprints).toHaveLength(1);
      
      const sprintInList = listResponse.body.data.sprints[0];
      expect(sprintInList.id).toBe(sprintId);
      expect(sprintInList.name).toBe(sprintName);
      expect(sprintInList.status).toBe('active');
      
      // Step 2: Verify the individual sprint endpoint also finds it
      // This is the regression test - it should NOT return 404
      const individualResponse = await request(app).get(`/api/agile/sprints/${sprintId}`);
      
      expect(individualResponse.status).toBe(200);
      expect(individualResponse.body.success).toBe(true);
      expect(individualResponse.body.data).toBeDefined();
      expect(individualResponse.body.data.id).toBe(sprintId);
      expect(individualResponse.body.data.name).toBe(sprintName);
      expect(individualResponse.body.data.status).toBe('active');
      
      // Verify field mapping is correct
      expect(individualResponse.body.data.startDate).toBeDefined();
      expect(individualResponse.body.data.endDate).toBeDefined();
      expect(individualResponse.body.data.team).toEqual(['Developer 1', 'Developer 2']);
      expect(individualResponse.body.data.storyPointsPlanned).toBe(20);
      // Note: storyPointsCompleted is recalculated based on actual stories
      // Since we haven't added any stories, it should be 0
      expect(individualResponse.body.data.storyPointsCompleted).toBe(0);
    });

    it('should handle complex sprint IDs with hyphens and UUIDs', async () => {
      // Test with a complex ID format similar to what was failing
      const complexSprintId = 'sprint-' + randomUUID();
      
      await testDb.run(`
        INSERT INTO agile_sprints (
          id, project_id, name, goal, status, 
          start_date, end_date, duration, team,
          created_at, updated_at
        ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      `, [
        complexSprintId,
        'default',
        'Sprint with Complex ID',
        'Test complex IDs',
        'active',
        Date.now(),
        Date.now() + 14 * 24 * 60 * 60 * 1000,
        14,
        '[]',
        Date.now(),
        Date.now()
      ]);

      // Should successfully retrieve the sprint with complex ID
      const response = await request(app).get(`/api/agile/sprints/${complexSprintId}`);
      
      expect(response.status).toBe(200);
      expect(response.body.success).toBe(true);
      expect(response.body.data.id).toBe(complexSprintId);
    });

    it('should properly return 404 for truly non-existent sprints', async () => {
      // Ensure we're testing a sprint that really doesn't exist
      const response = await request(app).get('/api/agile/sprints/definitely-does-not-exist');
      
      expect(response.status).toBe(404);
      expect(response.body.success).toBe(false);
      expect(response.body.error).toBe('Sprint not found');
      expect(response.body.sprintId).toBe('definitely-does-not-exist');
    });
  });
});