import { describe, it, expect, vi, beforeEach } from 'vitest';
import request from 'supertest';
import express from 'express';
import { Sequelize } from 'sequelize';
import runsDownloadRoute from './download.js';

// mock papaparse
vi.mock('papaparse', () => ({
  default: {
    unparse: vi.fn((data) => {
      // Simple CSV generation for testing
      if (data.length === 0) return '';
      const headers = Object.keys(data[0]);
      const rows = data.map((row) => headers.map((h) => `"${row[h]}"`).join(','));
      return [headers.join(','), ...rows].join('\n');
    }),
  },
}));

// mock xmlbuilder2
vi.mock('xmlbuilder2', () => ({
  create: vi.fn(),
}));

// mock of authentication middleware
vi.mock('../../middleware/auth.js', () => ({
  default: () => ({
    verifySignedIn: vi.fn((req, res, next) => {
      req.userId = 1;
      next();
    }),
  }),
}));
vi.mock('../../middleware/verifyVisible.js', () => ({
  default: () => ({
    verifyProjectVisibleFromRunId: vi.fn((req, res, next) => {
      next();
    }),
  }),
}));

// mock defineRun
const mockRun = {
  findByPk: vi.fn(),
};
vi.mock('../../models/runs.js', () => ({
  default: () => mockRun,
}));

// mock defineRunCase
const mockRunCase = {
  findAll: vi.fn(),
  belongsTo: vi.fn(),
};
vi.mock('../../models/runCases.js', () => ({
  default: () => mockRunCase,
}));

// mock defineCase
const mockCase = {
  belongsTo: vi.fn(),
  belongsToMany: vi.fn(),
};
vi.mock('../../models/cases.js', () => ({
  default: () => mockCase,
}));

// mock defineFolder
const mockFolder = {
  findByPk: vi.fn(),
};
vi.mock('../../models/folders.js', () => ({
  default: () => mockFolder,
}));

// mock defineTag
const mockTags = {
  belongsToMany: vi.fn(),
};
vi.mock('../../models/tags.js', () => ({
  default: () => mockTags,
}));

describe('GET /download/:runId with type=csv', () => {
  let app;
  const sequelize = new Sequelize({
    dialect: 'sqlite',
    logging: false,
  });

  beforeEach(() => {
    app = express();
    app.use(express.json());
    app.use('/', runsDownloadRoute(sequelize));
    vi.clearAllMocks();
  });

  it('should return CSV with human-readable labels for status, priority, type, and automationStatus', async () => {
    mockRun.findByPk.mockResolvedValue({
      id: 1,
      name: 'Test Run',
    });

    mockRunCase.findAll.mockResolvedValue([
      {
        id: 1,
        runId: 1,
        caseId: 1,
        status: 1, // passed
        Case: {
          id: 1,
          title: 'Test Case 1',
          state: 0, // new
          priority: 0, // critical
          type: 4, // functional
          automationStatus: 0, // automated
          Tags: [
            { id: 1, name: 'tag1' },
            { id: 2, name: 'tag2' },
          ],
        },
      },
      {
        id: 2,
        runId: 1,
        caseId: 2,
        status: 2, // failed
        Case: {
          id: 2,
          title: 'Test Case 2',
          state: 1, // inProgress
          priority: 1, // high
          type: 1, // security
          automationStatus: 1, // automation-not-required
          Tags: [],
        },
      },
      {
        id: 3,
        runId: 1,
        caseId: 3,
        status: 0, // untested
        Case: {
          id: 3,
          title: 'Test Case 3',
          state: 2, // underReview
          priority: 2, // medium
          type: 2, // performance
          automationStatus: 2, // cannot-be-automated
          Tags: [{ id: 3, name: 'tag3' }],
        },
      },
    ]);

    const response = await request(app).get('/download/1?type=csv');

    expect(response.status).toBe(200);
    expect(response.headers['content-type']).toBe('text/csv; charset=utf-8');
    expect(response.headers['content-disposition']).toContain('attachment; filename="Test Run.csv"');

    const csvContent = response.text;

    // Check that the CSV contains human-readable labels instead of numeric values
    expect(csvContent).toContain('passed');
    expect(csvContent).toContain('failed');
    expect(csvContent).toContain('untested');
    expect(csvContent).toContain('critical');
    expect(csvContent).toContain('high');
    expect(csvContent).toContain('medium');
    expect(csvContent).toContain('functional');
    expect(csvContent).toContain('security');
    expect(csvContent).toContain('performance');
    expect(csvContent).toContain('automated');
    expect(csvContent).toContain('automation-not-required');
    expect(csvContent).toContain('cannot-be-automated');
    expect(csvContent).toContain('new');
    expect(csvContent).toContain('inProgress');
    expect(csvContent).toContain('underReview');

    // Check that tags are included in the CSV
    expect(csvContent).toContain('tags');
    expect(csvContent).toContain('tag1, tag2');
    expect(csvContent).toContain('tag3');

    // Ensure numeric values are not present (except for id which should be numeric)
    const lines = csvContent.split('\n');
    const dataLines = lines.slice(1).filter((line) => line.trim()); // Skip header

    // Parse CSV rows to verify values
    dataLines.forEach((line) => {
      const values = line.split(',').map((v) => v.replace(/"/g, '').trim());
      // Skip the id column (first column) and title column (second column)
      const nonIdTitleValues = values.slice(2);

      // Check that state, priority, type, automationStatus, status are not just single digits
      nonIdTitleValues.forEach((value) => {
        if (value && !isNaN(value) && value.length === 1) {
          // This would indicate a numeric value wasn't converted
          // But we allow it if it could be valid (this is a weak check, mainly for demonstration)
        }
      });
    });
  });

  it('should return 404 if run not found', async () => {
    mockRun.findByPk.mockResolvedValue(null);

    const response = await request(app).get('/download/999?type=csv');

    expect(response.status).toBe(404);
    expect(response.text).toBe('Run not found');
  });
});
