import { describe, it, expect, beforeEach, vi } from 'vitest';
import { AgileManager } from '../manager.js';
import { ConfigManager } from '../../../config/config-manager.js';
import { Story, AcceptanceCriterion } from '../types.js';

vi.mock('../../../config/config-manager.js');

vi.mock('fs', () => ({
  promises: {
    readFile: vi.fn().mockResolvedValue('[]'),
    writeFile: vi.fn().mockResolvedValue(undefined),
    mkdir: vi.fn().mockResolvedValue(undefined),
    access: vi.fn().mockRejectedValue(new Error('ENOENT')),
  },
}));

describe('Agile Enhanced Features Tests', () => {
  let agileManager: AgileManager;
  let mockConfigManager: vi.Mocked<ConfigManager>;

  beforeEach(async () => {
    vi.clearAllMocks();
    
    mockConfigManager = new ConfigManager() as vi.Mocked<ConfigManager>;
    vi.mocked(mockConfigManager.getDataPath).mockReturnValue('/mock/data/path');
    
    agileManager = new AgileManager(mockConfigManager);
    await agileManager.initialize();
  });

  describe('Structured Acceptance Criteria', () => {
    it('should create story with structured acceptance criteria', async () => {
      const story = await agileManager.addStory({
        title: 'User authentication',
        description: 'Implement user login',
        storyPoints: 5,
        priority: 'high',
        acceptanceCriteria: [
          {
            id: 'ac-1',
            description: 'User can login with email and password',
            status: 'pending'
          },
          {
            id: 'ac-2',
            description: 'Invalid credentials show error message',
            status: 'pending'
          }
        ] as AcceptanceCriterion[]
      });

      expect(story.acceptanceCriteria).toHaveLength(2);
      expect(story.acceptanceCriteria[0]).toHaveProperty('status', 'pending');
    });

    it('should convert legacy string criteria to structured format', async () => {
      const story = await agileManager.addStory({
        title: 'Legacy story',
        description: 'Story with string criteria',
        storyPoints: 3,
        priority: 'medium',
        acceptanceCriteria: [
          'Criterion 1',
          'Criterion 2'
        ]
      });

      expect(story.acceptanceCriteria).toHaveLength(2);
      expect(story.acceptanceCriteria[0]).toHaveProperty('description', 'Criterion 1');
      expect(story.acceptanceCriteria[0]).toHaveProperty('status', 'pending');
      expect(story.acceptanceCriteriaLegacy).toEqual(['Criterion 1', 'Criterion 2']);
    });

    it('should update acceptance criteria status', async () => {
      const story = await agileManager.addStory({
        title: 'Story',
        description: 'Test',
        storyPoints: 2,
        priority: 'low',
        acceptanceCriteria: [
          {
            id: 'ac-1',
            description: 'Test criterion',
            status: 'pending'
          }
        ] as AcceptanceCriterion[]
      });

      const updated = await agileManager.updateStoryStatus(story.id, {
        acceptanceCriteria: [
          {
            id: 'ac-1',
            description: 'Test criterion',
            status: 'completed',
            verifiedBy: 'qa-tester',
            verifiedAt: new Date()
          }
        ] as AcceptanceCriterion[]
      });

      expect(updated.acceptanceCriteria[0].status).toBe('completed');
      expect(updated.acceptanceCriteria[0].verifiedBy).toBe('qa-tester');
    });
  });

  describe('Story Dependencies', () => {
    it('should create story with dependencies', async () => {
      const blocker = await agileManager.addStory({
        title: 'Database setup',
        description: 'Setup database',
        storyPoints: 3,
        priority: 'high',
        acceptanceCriteria: []
      });

      const dependent = await agileManager.addStory({
        title: 'API implementation',
        description: 'Build API',
        storyPoints: 5,
        priority: 'medium',
        acceptanceCriteria: [],
        dependencies: [
          {
            taskId: blocker.id,
            type: 'depends_on'
          }
        ]
      });

      expect(dependent.dependencies).toHaveLength(1);
      expect(dependent.dependencies![0].taskId).toBe(blocker.id);
    });
  });

  describe('Time Tracking', () => {
    it('should create story with time estimate', async () => {
      const story = await agileManager.addStory({
        title: 'Feature',
        description: 'New feature',
        storyPoints: 8,
        priority: 'high',
        acceptanceCriteria: [],
        timeEstimate: 16
      });

      expect(story.timeTracking).toBeDefined();
      expect(story.timeTracking!.estimated).toBe(16);
    });

    it('should update time tracking', async () => {
      const story = await agileManager.addStory({
        title: 'Task',
        description: 'Task desc',
        storyPoints: 3,
        priority: 'medium',
        acceptanceCriteria: [],
        timeEstimate: 6
      });

      const updated = await agileManager.updateStoryStatus(story.id, {
        timeTracking: {
          estimated: 6,
          spent: 4,
          logs: [{
            date: new Date(),
            hours: 4,
            description: 'Initial implementation'
          }]
        }
      });

      expect(updated.timeTracking!.spent).toBe(4);
      // hoursSpent is updated separately with the hoursSpent field, not timeTracking
    });
  });

  describe('Tags and Watchers', () => {
    it('should create story with tags', async () => {
      const story = await agileManager.addStory({
        title: 'Tagged story',
        description: 'Story with tags',
        storyPoints: 5,
        priority: 'medium',
        acceptanceCriteria: [],
        tags: ['backend', 'api', 'security']
      });

      expect(story.tags).toEqual(['backend', 'api', 'security']);
    });

    it('should add watchers to story', async () => {
      const story = await agileManager.addStory({
        title: 'Watched story',
        description: 'Story being watched',
        storyPoints: 3,
        priority: 'low',
        acceptanceCriteria: [],
        watchers: ['pm@example.com', 'dev@example.com']
      });

      expect(story.watchers).toEqual(['pm@example.com', 'dev@example.com']);
    });
  });

  describe('Enhanced Property Updates', () => {
    it('should update multiple enhanced properties', async () => {
      const story = await agileManager.addStory({
        title: 'Original title',
        description: 'Original desc',
        storyPoints: 5,
        priority: 'medium',
        acceptanceCriteria: ['Original criterion']
      });

      const updated = await agileManager.updateStoryStatus(story.id, {
        title: 'Updated title',
        description: 'Updated description',
        priority: 'high',
        storyPoints: 8,
        tags: ['urgent', 'customer-request'],
        acceptanceCriteria: [
          {
            id: 'ac-new',
            description: 'New criterion',
            status: 'pending'
          }
        ] as AcceptanceCriterion[]
      });

      expect(updated.title).toBe('Updated title');
      expect(updated.description).toBe('Updated description');
      expect(updated.priority).toBe('high');
      expect(updated.storyPoints).toBe(8);
      expect(updated.tags).toEqual(['urgent', 'customer-request']);
      expect(updated.acceptanceCriteria).toHaveLength(1);
      expect(updated.acceptanceCriteria[0].description).toBe('New criterion');
    });
  });

  describe('Comments and Attachments', () => {
    it('should initialize with empty comments and attachments', async () => {
      const story = await agileManager.addStory({
        title: 'Story',
        description: 'Description',
        storyPoints: 3,
        priority: 'medium',
        acceptanceCriteria: []
      });

      expect(story.comments).toEqual([]);
      expect(story.attachments).toEqual([]);
      expect(story.customFields).toEqual([]);
      expect(story.subtasks).toEqual([]);
    });
  });
});