import { describe, it, expect } from 'vitest';
import {
  getNygardTemplate,
  getMADRTemplate,
  getYStatementTemplate,
  renderADRTemplate,
  getTemplateFields,
  validateTemplateData
} from '../templates.js';
import type { ADR, ADRTemplate } from '../types.js';

describe('ADR Templates', () => {
  const baseADR: ADR = {
    id: 'ADR-0001',
    title: 'Use React for Frontend Framework',
    status: 'accepted',
    date: new Date('2024-01-15'),
    deciders: ['John Doe', 'Jane Smith'],
    template: 'nygard',
    context: 'We need to choose a frontend framework for our new web application.',
    decision: 'We will use React as our frontend framework.',
    consequences: 'The team will need to learn React. We will have access to a large ecosystem.',
    createdAt: new Date('2024-01-15'),
    updatedAt: new Date('2024-01-15'),
    statusHistory: []
  };

  describe('getNygardTemplate', () => {
    it('should return Nygard template structure', () => {
      const template = getNygardTemplate();

      expect(template.name).toBe('nygard');
      expect(template.description).toContain('Michael Nygard');
      expect(template.fields).toContain('title');
      expect(template.fields).toContain('status');
      expect(template.fields).toContain('context');
      expect(template.fields).toContain('decision');
      expect(template.fields).toContain('consequences');
    });

    it('should include required and optional fields', () => {
      const template = getNygardTemplate();

      expect(template.requiredFields).toContain('title');
      expect(template.requiredFields).toContain('context');
      expect(template.requiredFields).toContain('decision');
      expect(template.optionalFields).toContain('tags');
      expect(template.optionalFields).toContain('relatedTo');
    });
  });

  describe('getMADRTemplate', () => {
    it('should return MADR template structure', () => {
      const template = getMADRTemplate();

      expect(template.name).toBe('madr');
      expect(template.description).toContain('Markdown Architectural Decision Records');
      expect(template.fields).toContain('decisionDrivers');
      expect(template.fields).toContain('consideredOptions');
    });

    it('should include MADR-specific fields', () => {
      const template = getMADRTemplate();

      expect(template.requiredFields).toContain('decisionDrivers');
      expect(template.fields).toContain('prosAndCons');
      expect(template.fields).toContain('links');
    });
  });

  describe('getYStatementTemplate', () => {
    it('should return Y-statement template structure', () => {
      const template = getYStatementTemplate();

      expect(template.name).toBe('y-statement');
      expect(template.description).toContain('simple format');
      expect(template.fields).toHaveLength(8); // Fewer fields than other templates
    });

    it('should have minimal required fields', () => {
      const template = getYStatementTemplate();

      expect(template.requiredFields).toContain('title');
      expect(template.requiredFields).toContain('context');
      expect(template.requiredFields).toContain('decision');
      expect(template.requiredFields).toContain('consequences');
      expect(template.requiredFields).not.toContain('decisionDrivers');
    });
  });

  describe('renderADRTemplate', () => {
    it('should render Nygard template correctly', () => {
      const rendered = renderADRTemplate(baseADR);

      expect(rendered).toContain('# ADR-0001: Use React for Frontend Framework');
      expect(rendered).toContain('## Status');
      expect(rendered).toContain('Accepted');
      expect(rendered).toContain('## Context');
      expect(rendered).toContain(baseADR.context);
      expect(rendered).toContain('## Decision');
      expect(rendered).toContain(baseADR.decision);
      expect(rendered).toContain('## Consequences');
      expect(rendered).toContain(baseADR.consequences);
    });

    it('should render MADR template with additional fields', () => {
      const madrADR: ADR = {
        ...baseADR,
        template: 'madr',
        decisionDrivers: ['Performance', 'Developer Experience', 'Community Support'],
        consideredOptions: [
          {
            title: 'React',
            description: 'A JavaScript library for building user interfaces',
            pros: ['Large ecosystem', 'Good performance', 'Wide adoption'],
            cons: ['Learning curve', 'Requires build tooling']
          },
          {
            title: 'Vue',
            description: 'The Progressive JavaScript Framework',
            pros: ['Easy to learn', 'Good documentation', 'Flexible'],
            cons: ['Smaller ecosystem', 'Less job market demand']
          }
        ]
      };

      const rendered = renderADRTemplate(madrADR);

      expect(rendered).toContain('## Decision Drivers');
      expect(rendered).toContain('* Performance');
      expect(rendered).toContain('* Developer Experience');
      expect(rendered).toContain('## Considered Options');
      expect(rendered).toContain('* React - A JavaScript library');
      expect(rendered).toContain('* Vue - The Progressive JavaScript Framework');
    });

    it('should render Y-statement template simply', () => {
      const yStatementADR: ADR = {
        ...baseADR,
        template: 'y-statement'
      };

      const rendered = renderADRTemplate(yStatementADR);

      expect(rendered).toContain('# ADR-0001: Use React for Frontend Framework');
      expect(rendered).toContain('## Status: Accepted');
      expect(rendered).toContain(baseADR.context);
      expect(rendered).toContain(baseADR.decision);
      expect(rendered).toContain(baseADR.consequences);
      expect(rendered).not.toContain('## Decision Drivers'); // Y-statement doesn't have this
    });

    it('should include metadata section', () => {
      const rendered = renderADRTemplate(baseADR);

      expect(rendered).toContain('**Date**: 2024-01-15');
      expect(rendered).toContain('**Deciders**: John Doe, Jane Smith');
    });

    it('should include status history if present', () => {
      const adrWithHistory: ADR = {
        ...baseADR,
        statusHistory: [
          {
            from: 'proposed',
            to: 'accepted',
            date: new Date('2024-01-20'),
            changedBy: 'John Doe',
            reason: 'Approved by architecture board'
          }
        ]
      };

      const rendered = renderADRTemplate(adrWithHistory);

      expect(rendered).toContain('## Status History');
      expect(rendered).toContain('proposed → accepted');
      expect(rendered).toContain('2024-01-20');
      expect(rendered).toContain('John Doe');
      expect(rendered).toContain('Approved by architecture board');
    });

    it('should include tags if present', () => {
      const adrWithTags: ADR = {
        ...baseADR,
        tags: ['frontend', 'react', 'framework']
      };

      const rendered = renderADRTemplate(adrWithTags);

      expect(rendered).toContain('**Tags**: frontend, react, framework');
    });

    it('should include relationships if present', () => {
      const adrWithRelations: ADR = {
        ...baseADR,
        supersedes: ['ADR-0000'],
        relatedTo: ['ADR-0002', 'ADR-0003']
      };

      const rendered = renderADRTemplate(adrWithRelations);

      expect(rendered).toContain('## Links');
      expect(rendered).toContain('* Supersedes: ADR-0000');
      expect(rendered).toContain('* Related to: ADR-0002, ADR-0003');
    });

    it('should render options with pros and cons for MADR', () => {
      const madrADR: ADR = {
        ...baseADR,
        template: 'madr',
        consideredOptions: [
          {
            title: 'React',
            description: 'Facebook\'s library',
            pros: ['Popular', 'Fast'],
            cons: ['Complex']
          }
        ],
        prosAndCons: [
          {
            option: 'React',
            pros: ['Great ecosystem'],
            cons: ['Steep learning curve']
          }
        ]
      };

      const rendered = renderADRTemplate(madrADR);

      expect(rendered).toContain('### React');
      expect(rendered).toContain('Facebook\'s library');
      expect(rendered).toContain('* Good, because Popular');
      expect(rendered).toContain('* Bad, because Complex');
    });

    it('should format dates consistently', () => {
      const rendered = renderADRTemplate(baseADR);
      
      // Should use ISO date format
      expect(rendered).toMatch(/\*\*Date\*\*: \d{4}-\d{2}-\d{2}/);
    });
  });

  describe('getTemplateFields', () => {
    it('should return fields for Nygard template', () => {
      const fields = getTemplateFields('nygard');

      expect(fields.required).toContain('title');
      expect(fields.required).toContain('context');
      expect(fields.required).toContain('decision');
      expect(fields.required).toContain('consequences');
      expect(fields.optional).toContain('tags');
    });

    it('should return fields for MADR template', () => {
      const fields = getTemplateFields('madr');

      expect(fields.required).toContain('decisionDrivers');
      expect(fields.optional).toContain('consideredOptions');
      expect(fields.optional).toContain('prosAndCons');
    });

    it('should return fields for Y-statement template', () => {
      const fields = getTemplateFields('y-statement');

      expect(fields.required).toContain('title');
      expect(fields.required).toContain('context');
      expect(fields.required).toContain('decision');
      expect(fields.optional).toContain('tags');
    });

    it('should throw error for invalid template', () => {
      expect(() => getTemplateFields('invalid' as ADRTemplate))
        .toThrow('Unknown template: invalid');
    });
  });

  describe('validateTemplateData', () => {
    it('should validate Nygard template data', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: ['John']
      };

      const result = validateTemplateData('nygard', data);

      expect(result.valid).toBe(true);
      expect(result.errors).toHaveLength(0);
    });

    it('should catch missing required fields', () => {
      const data = {
        title: 'Test ADR',
        // Missing context
        decision: 'Test decision',
        consequences: 'Test consequences'
      };

      const result = validateTemplateData('nygard', data);

      expect(result.valid).toBe(false);
      expect(result.errors).toContain('Missing required field: context');
    });

    it('should validate MADR specific fields', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: ['John'],
        decisionDrivers: ['Performance', 'Cost']
      };

      const result = validateTemplateData('madr', data);

      expect(result.valid).toBe(true);
    });

    it('should validate array fields', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: 'John' // Should be array
      };

      const result = validateTemplateData('nygard', data);

      expect(result.valid).toBe(false);
      expect(result.errors).toContain('Field "deciders" must be an array');
    });

    it('should validate consideredOptions structure', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: ['John'],
        decisionDrivers: ['Performance'],
        consideredOptions: [
          {
            title: 'Option 1',
            // Missing description
            pros: ['Good'],
            cons: ['Bad']
          }
        ]
      };

      const result = validateTemplateData('madr', data);

      expect(result.valid).toBe(false);
      expect(result.errors).toContain('Option must have title, description, pros, and cons');
    });

    it('should allow optional fields to be missing', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: ['John']
        // No tags, relatedTo, etc.
      };

      const result = validateTemplateData('nygard', data);

      expect(result.valid).toBe(true);
    });

    it('should validate Y-statement minimal requirements', () => {
      const data = {
        title: 'Simple Decision',
        context: 'We need to decide',
        decision: 'We choose this',
        consequences: 'This will happen',
        deciders: ['Team']
      };

      const result = validateTemplateData('y-statement', data);

      expect(result.valid).toBe(true);
    });

    it('should validate empty arrays', () => {
      const data = {
        title: 'Test ADR',
        context: 'Test context',
        decision: 'Test decision',
        consequences: 'Test consequences',
        deciders: [] // Empty array
      };

      const result = validateTemplateData('nygard', data);

      expect(result.valid).toBe(false);
      expect(result.errors).toContain('Field "deciders" cannot be empty');
    });
  });
});