import { AutoReporter } from '../reporters/AutoReporter.js';
/**
 * TESTS UNITAIRES COMPLETS - APIController
 * Tests isolés avec mocks pour valider chaque méthode
 */

const { describe, it, beforeEach, afterEach, mock } = require('node:test');
const assert = require('node:assert');
const { APIController } = require('../../lib/APIController');

// Mock des dépendances
const mockGetPersonalities = mock.fn();
const mockReadInstructionsData = mock.fn();
const mockGetStoredArticle = mock.fn();
const mockGetRecentArticles = mock.fn();
const mockHandleFullWorkflow = mock.fn();

// Patch des modules
mock.module('../../lib/BrainConfig', () => ({
  getPersonalities: mockGetPersonalities,
  readInstructionsData: mockReadInstructionsData
}));

mock.module('../../lib/ArticleStorage', () => ({
  getStoredArticle: mockGetStoredArticle,
  getRecentArticles: mockGetRecentArticles
}));

mock.module('../../lib/Main', () => ({
  handleFullWorkflow: mockHandleFullWorkflow
}));


// Auto-Reporter Configuration
const autoReporter = new AutoReporter();

describe('APIController - Tests Unitaires Complets', () => {
  let apiController;
  let mockReq, mockRes;

  beforeEach(() => {
    apiController = new APIController();

    // Mock response object complet
    mockRes = {
      data: null,
      statusCode: 200,
      headers: {},
      json: mock.fn((data) => { mockRes.data = data; return mockRes; }),
      status: mock.fn((code) => { mockRes.statusCode = code; return mockRes; }),
      setHeader: mock.fn((key, value) => { mockRes.headers[key] = value; return mockRes; }),
      send: mock.fn((data) => { mockRes.sentData = data; return mockRes; })
    };

    // Reset mocks
    mockGetPersonalities.mock.resetCalls();
    mockReadInstructionsData.mock.resetCalls();
    mockGetStoredArticle.mock.resetCalls();
    mockGetRecentArticles.mock.resetCalls();
    mockHandleFullWorkflow.mock.resetCalls();
  });

  describe('🏥 Health Check', () => {
    it('should return healthy status with all required fields', async () => {
      mockReq = {};

      await apiController.getHealth(mockReq, mockRes);

      assert.strictEqual(mockRes.json.mock.callCount(), 1);
      const response = mockRes.json.mock.calls[0].arguments[0];

      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.status, 'healthy');
      assert.ok(response.data.timestamp);
      assert.ok(response.data.version);
      assert.ok(typeof response.data.uptime === 'number');
      assert.ok(response.data.memory);
      assert.ok(response.data.environment);
    });

    it('should handle health check errors gracefully', async () => {
      // Forcer une erreur en cassant process.uptime
      const originalUptime = process.uptime;
      process.uptime = () => { throw new Error('Process error'); };

      await apiController.getHealth(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.callCount(), 1);
      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 500);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error);

      // Restaurer
      process.uptime = originalUptime;
    });
  });

  describe('📊 Metrics', () => {
    it('should return complete system metrics', async () => {
      mockReq = {};

      await apiController.getMetrics(mockReq, mockRes);

      const response = mockRes.json.mock.calls[0].arguments[0];

      assert.strictEqual(response.success, true);
      assert.ok(response.data.articles);
      assert.ok(response.data.projects);
      assert.ok(response.data.templates);
      assert.ok(response.data.system);

      // Vérifier structure des métriques
      assert.ok(typeof response.data.articles.total === 'number');
      assert.ok(typeof response.data.projects.total === 'number');
      assert.ok(typeof response.data.templates.total === 'number');
      assert.ok(response.data.system.uptime !== undefined);
      assert.ok(response.data.system.memory);
      assert.ok(response.data.system.platform);
      assert.ok(response.data.system.nodeVersion);
    });
  });

  describe('📁 Gestion Projets', () => {
    it('should create project with valid data', async () => {
      mockReq = {
        body: {
          name: 'Test Project',
          description: 'Description test',
          config: { option: 'value' }
        }
      };

      await apiController.createProject(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.callCount(), 1);
      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 201);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.name, 'Test Project');
      assert.strictEqual(response.data.description, 'Description test');
      assert.ok(response.data.id);
      assert.ok(response.data.createdAt);
      assert.strictEqual(response.data.articlesCount, 0);
    });

    it('should reject project creation without name', async () => {
      mockReq = {
        body: {
          description: 'Sans nom'
        }
      };

      await apiController.createProject(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 400);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error.includes('Nom du projet requis'));
    });

    it('should return empty project list initially', async () => {
      mockReq = {};

      await apiController.getProjects(mockReq, mockRes);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.ok(Array.isArray(response.data.projects));
      assert.strictEqual(response.data.total, 0);
    });

    it('should return projects after creation', async () => {
      // Créer un projet d'abord
      await apiController.createProject({
        body: { name: 'Project 1', description: 'Desc 1' }
      }, mockRes);

      // Puis récupérer la liste
      mockReq = {};
      await apiController.getProjects(mockReq, mockRes);

      const response = mockRes.json.mock.calls[1].arguments[0]; // Deuxième appel
      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.projects.length, 1);
      assert.strictEqual(response.data.total, 1);
      assert.strictEqual(response.data.projects[0].name, 'Project 1');
    });
  });

  describe('📋 Gestion Templates', () => {
    it('should create template with complete data', async () => {
      mockReq = {
        body: {
          name: 'Template Test',
          content: '<?xml version="1.0"?><template></template>',
          description: 'Template de test',
          category: 'test'
        }
      };

      await apiController.createTemplate(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 201);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.name, 'Template Test');
      assert.strictEqual(response.data.category, 'test');
      assert.ok(response.data.id);
      assert.ok(response.data.createdAt);
    });

    it('should reject template without name', async () => {
      mockReq = {
        body: {
          content: '<template></template>'
        }
      };

      await apiController.createTemplate(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 400);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error.includes('Nom et contenu du template requis'));
    });

    it('should reject template without content', async () => {
      mockReq = {
        body: {
          name: 'Template sans contenu'
        }
      };

      await apiController.createTemplate(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 400);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error.includes('Nom et contenu du template requis'));
    });

    it('should use default category when not provided', async () => {
      mockReq = {
        body: {
          name: 'Template sans catégorie',
          content: '<template></template>'
        }
      };

      await apiController.createTemplate(mockReq, mockRes);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.data.category, 'custom');
    });
  });

  describe('📝 Gestion Articles', () => {
    it('should validate article creation input', async () => {
      mockReq = {
        body: {}
      };

      await apiController.createArticle(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 400);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error.includes('Mot-clé ou numéro de ligne requis'));
    });

    it('should create article with keyword', async () => {
      mockHandleFullWorkflow.mock.mockImplementationOnce(async () => ({
        id: 'article_123',
        slug: 'test-article',
        content: 'Contenu généré'
      }));

      mockReq = {
        body: {
          keyword: 'test keyword',
          project: 'test-project',
          config: {
            selectiveStack: 'standardEnhancement'
          }
        }
      };

      await apiController.createArticle(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 201);
      assert.strictEqual(mockHandleFullWorkflow.mock.callCount(), 1);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.ok(response.data.id);
      assert.ok(response.data.article);
    });

    it('should create article with row number', async () => {
      mockHandleFullWorkflow.mock.mockImplementationOnce(async () => ({
        id: 'article_456',
        content: 'Contenu de la ligne 5'
      }));

      mockReq = {
        body: {
          rowNumber: 5,
          project: 'sheets-project'
        }
      };

      await apiController.createArticle(mockReq, mockRes);

      assert.strictEqual(mockHandleFullWorkflow.mock.callCount(), 1);
      const workflowArgs = mockHandleFullWorkflow.mock.calls[0].arguments[0];
      assert.strictEqual(workflowArgs.rowNumber, 5);
      assert.strictEqual(workflowArgs.project, 'sheets-project');
      assert.strictEqual(workflowArgs.source, 'api');
    });

    it('should handle article creation errors', async () => {
      mockHandleFullWorkflow.mock.mockImplementationOnce(async () => {
        throw new Error('Workflow failed');
      });

      mockReq = {
        body: {
          keyword: 'test fail'
        }
      };

      await apiController.createArticle(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 500);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
      assert.ok(response.error);
    });

    it('should get articles with default pagination', async () => {
      mockGetRecentArticles.mock.mockImplementationOnce(async () => [
        { id: '1', title: 'Article 1' },
        { id: '2', title: 'Article 2' }
      ]);

      mockReq = {
        query: {}
      };

      await apiController.getArticles(mockReq, mockRes);

      assert.strictEqual(mockGetRecentArticles.mock.callCount(), 1);
      assert.strictEqual(mockGetRecentArticles.mock.calls[0].arguments[0], 50); // limit par défaut

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.articles.length, 2);
      assert.strictEqual(response.data.total, 2);
      assert.strictEqual(response.data.limit, 50);
      assert.strictEqual(response.data.offset, 0);
    });

    it('should get articles with custom pagination', async () => {
      mockGetRecentArticles.mock.mockImplementationOnce(async () => [
        { id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }, { id: '5' }
      ]);

      mockReq = {
        query: {
          limit: '3',
          offset: '1'
        }
      };

      await apiController.getArticles(mockReq, mockRes);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.data.limit, 3);
      assert.strictEqual(response.data.offset, 1);
      assert.strictEqual(response.data.articles.length, 3);
    });

    it('should handle article retrieval error', async () => {
      mockGetStoredArticle.mock.mockImplementationOnce(async () => {
        throw new Error('Google Sheets error');
      });

      mockReq = {
        params: { id: 'test_id' },
        query: {}
      };

      await apiController.getArticle(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 500);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
    });
  });

  describe('⚙️ Configuration', () => {
    it('should get personalities config', async () => {
      mockGetPersonalities.mock.mockImplementationOnce(async () => [
        { nom: 'Marc', style: 'professionnel' },
        { nom: 'Sophie', style: 'familier' }
      ]);

      mockReq = {};

      await apiController.getPersonalitiesConfig(mockReq, mockRes);

      assert.strictEqual(mockGetPersonalities.mock.callCount(), 1);

      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, true);
      assert.strictEqual(response.data.personalities.length, 2);
      assert.strictEqual(response.data.total, 2);
    });

    it('should handle personalities config error', async () => {
      mockGetPersonalities.mock.mockImplementationOnce(async () => {
        throw new Error('Google Sheets unavailable');
      });

      mockReq = {};

      await apiController.getPersonalitiesConfig(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 500);
      const response = mockRes.json.mock.calls[0].arguments[0];
      assert.strictEqual(response.success, false);
    });
  });

  describe('🔍 Edge Cases et Validation', () => {
    it('should handle missing query parameters gracefully', async () => {
      mockReq = {
        // Pas de query
      };

      await apiController.getHealth(mockReq, mockRes);

      // Ne devrait pas planter
      assert.strictEqual(mockRes.json.mock.callCount(), 1);
    });

    it('should handle malformed request bodies', async () => {
      mockReq = {
        body: null
      };

      await apiController.createProject(mockReq, mockRes);

      assert.strictEqual(mockRes.status.mock.calls[0].arguments[0], 400);
    });

    it('should validate article format parameter', async () => {
      mockGetStoredArticle.mock.mockImplementationOnce(async () => ({
        id: 'test',
        content: 'Contenu test',
        htmlContent: '<p>Contenu HTML</p>',
        textContent: 'Contenu texte'
      }));

      // Test format HTML
      mockReq = {
        params: { id: 'test' },
        query: { format: 'html' }
      };

      await apiController.getArticle(mockReq, mockRes);

      assert.strictEqual(mockRes.setHeader.mock.callCount(), 1);
      assert.strictEqual(mockRes.setHeader.mock.calls[0].arguments[0], 'Content-Type');
      assert.strictEqual(mockRes.send.mock.callCount(), 1);
    });
  });
});

console.log('🧪 Tests Unitaires APIController - Validation complète des méthodes');