import {beforeEach, describe, expect, it} from 'vitest';
import {factories} from '../../../../../test/helpers';
import {EventService} from '../../services/EventService';
import {WorkflowService} from '../../services/WorkflowService';

describe('Workflows Controller', () => {
  let projectId: string;

  beforeEach(async () => {
    const {project} = await factories.createUserWithProject();
    projectId = project.id;
  });

  // ========================================
  // GET /workflows/fields
  // ========================================
  describe('GET /workflows/fields', () => {
    it('should return contact and event fields', async () => {
      const contact = await factories.createContact({
        projectId,
        data: {
          firstName: 'John',
          lastName: 'Doe',
          plan: 'premium',
        },
      });

      // Create email events with data
      await EventService.trackEvent(projectId, 'email.opened', contact.id, undefined, {
        subject: 'Welcome',
        from: 'hello@example.com',
        openedAt: new Date().toISOString(),
        isFirstOpen: true,
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId);

      expect(responseBody.fields).toBeInstanceOf(Array);
      expect(responseBody.count).toBeGreaterThan(0);

      // Should include standard contact fields
      expect(responseBody.fields).toContain('contact.email');
      expect(responseBody.fields).toContain('contact.subscribed');

      // Should include custom contact data fields
      expect(responseBody.fields).toContain('contact.data.firstName');
      expect(responseBody.fields).toContain('contact.data.lastName');
      expect(responseBody.fields).toContain('contact.data.plan');

      // Should include event fields
      expect(responseBody.fields).toContain('event.subject');
      expect(responseBody.fields).toContain('event.from');
      expect(responseBody.fields).toContain('event.openedAt');
      expect(responseBody.fields).toContain('event.isFirstOpen');
    });

    it('should filter event fields by eventName query param', async () => {
      const contact = await factories.createContact({projectId});

      // Create email.opened events
      await EventService.trackEvent(projectId, 'email.opened', contact.id, undefined, {
        subject: 'Test',
        openedAt: new Date().toISOString(),
        isFirstOpen: true,
      });

      // Create email.clicked events
      await EventService.trackEvent(projectId, 'email.clicked', contact.id, undefined, {
        subject: 'Test',
        link: 'https://example.com',
        clickedAt: new Date().toISOString(),
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId, 'email.opened');

      // Should include email.opened fields
      expect(responseBody.fields).toContain('event.subject');
      expect(responseBody.fields).toContain('event.openedAt');
      expect(responseBody.fields).toContain('event.isFirstOpen');

      // Should NOT include email.clicked fields
      expect(responseBody.fields).not.toContain('event.link');
      expect(responseBody.fields).not.toContain('event.clickedAt');
    });

    it('should return fields in sorted order', async () => {
      const contact = await factories.createContact({
        projectId,
        data: {
          zebra: 'last',
          apple: 'first',
        },
      });

      await EventService.trackEvent(projectId, 'test.event', contact.id, undefined, {
        zoo: 'value',
        aardvark: 'value',
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId);

      const fields = responseBody.fields as string[];

      // Verify fields are sorted
      const sortedFields = [...fields].sort();
      expect(fields).toEqual(sortedFields);
    });

    it('should return empty event fields when no events exist', async () => {
      const responseBody = await WorkflowService.getAvailableFields(projectId);

      // Should still have contact fields
      expect(responseBody.fields).toContain('contact.email');
      expect(responseBody.fields).toContain('contact.subscribed');

      // But no event fields
      const eventFields = responseBody.fields.filter((f: string) => f.startsWith('event.'));
      expect(eventFields).toHaveLength(0);
    });

    it('should only return fields for authenticated project', async () => {
      // Create another project with events
      const {project: otherProject} = await factories.createUserWithProject();
      const otherContact = await factories.createContact({projectId: otherProject.id});

      await EventService.trackEvent(otherProject.id, 'other.event', otherContact.id, undefined, {
        secretField: 'secret value',
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId);

      // Should not include fields from other project
      expect(responseBody.fields).not.toContain('event.secretField');
    });

    it('should handle URL encoded event names', async () => {
      const contact = await factories.createContact({projectId});

      await EventService.trackEvent(projectId, 'email.opened', contact.id, undefined, {
        field1: 'value',
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId, 'email.opened');

      expect(responseBody.fields).toContain('event.field1');
    });
  });

  // ========================================
  // Integration Tests
  // ========================================
  describe('Workflow field discovery integration', () => {
    it('should discover correct fields for email.sent events', async () => {
      const contact = await factories.createContact({projectId});
      const email = await factories.createEmail(projectId, contact.id);

      // Create email.sent event with actual structure
      await EventService.trackEvent(projectId, 'email.sent', contact.id, email.id, {
        subject: 'Welcome Email',
        from: 'hello@example.com',
        fromName: 'Example Team',
        messageId: 'msg-123',
        templateId: 'tpl-456',
        campaignId: null,
        sourceType: 'TRANSACTIONAL',
        sentAt: new Date().toISOString(),
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId, 'email.sent');

      expect(responseBody.fields).toContain('event.subject');
      expect(responseBody.fields).toContain('event.from');
      expect(responseBody.fields).toContain('event.fromName');
      expect(responseBody.fields).toContain('event.messageId');
      expect(responseBody.fields).toContain('event.templateId');
      expect(responseBody.fields).toContain('event.sourceType');
      expect(responseBody.fields).toContain('event.sentAt');
    });

    it('should discover correct fields for email.opened events', async () => {
      const contact = await factories.createContact({projectId});
      const email = await factories.createEmail(projectId, contact.id);

      await EventService.trackEvent(projectId, 'email.opened', contact.id, email.id, {
        subject: 'Newsletter',
        from: 'news@example.com',
        fromName: 'Newsletter Team',
        messageId: 'msg-456',
        openedAt: new Date().toISOString(),
        opens: 1,
        isFirstOpen: true,
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId, 'email.opened');

      expect(responseBody.fields).toContain('event.openedAt');
      expect(responseBody.fields).toContain('event.opens');
      expect(responseBody.fields).toContain('event.isFirstOpen');
    });

    it('should discover correct fields for email.clicked events', async () => {
      const contact = await factories.createContact({projectId});
      const email = await factories.createEmail(projectId, contact.id);

      await EventService.trackEvent(projectId, 'email.clicked', contact.id, email.id, {
        subject: 'Sale Announcement',
        from: 'sales@example.com',
        link: 'https://example.com/sale',
        clickedAt: new Date().toISOString(),
        clicks: 1,
        isFirstClick: true,
      });

      const responseBody = await WorkflowService.getAvailableFields(projectId, 'email.clicked');

      expect(responseBody.fields).toContain('event.link');
      expect(responseBody.fields).toContain('event.clickedAt');
      expect(responseBody.fields).toContain('event.clicks');
      expect(responseBody.fields).toContain('event.isFirstClick');
    });
  });
});
