/**
 * @ai-summary Integration tests for Documents API endpoints
 * @ai-context Tests full API flow with auth, database, and storage
 */

import request from 'supertest';
import { FastifyInstance } from 'fastify';
import { build } from '../../../../app';
import path from 'path';
import fs from 'fs';

describe('Documents Integration Tests', () => {
  let app: FastifyInstance;
  let testUserId: string;
  let testVehicleId: string;
  let authToken: string;

  beforeAll(async () => {
    app = build({ logger: false });
    await app.ready();

    // Create test user context
    testUserId = 'test-user-' + Date.now();
    authToken = 'Bearer test-token';

    // Create test vehicle for document association
    const vehicleData = {
      vin: '1HGBH41JXMN109186',
      nickname: 'Test Car',
      color: 'Blue',
      odometerReading: 50000,
    };

    const vehicleResponse = await request(app.server)
      .post('/api/vehicles')
      .set('Authorization', authToken)
      .send(vehicleData);

    testVehicleId = vehicleResponse.body.id;
  });

  afterAll(async () => {
    await app.close();
  });

  describe('POST /api/documents', () => {
    it('should create document metadata', async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Car Insurance Policy',
        notes: 'Annual policy',
        details: { provider: 'State Farm', policy_number: '12345' },
        issued_date: '2024-01-01',
        expiration_date: '2024-12-31',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData)
        .expect(201);

      expect(response.body).toMatchObject({
        id: expect.any(String),
        user_id: testUserId,
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Car Insurance Policy',
        notes: 'Annual policy',
        details: { provider: 'State Farm', policy_number: '12345' },
        issued_date: '2024-01-01',
        expiration_date: '2024-12-31',
        created_at: expect.any(String),
        updated_at: expect.any(String),
      });

      // Storage fields should be null initially
      expect(response.body.storage_bucket).toBeNull();
      expect(response.body.storage_key).toBeNull();
      expect(response.body.file_name).toBeNull();
    });

    it('should reject document for non-existent vehicle', async () => {
      const documentData = {
        vehicle_id: 'non-existent-vehicle',
        document_type: 'registration',
        title: 'Invalid Document',
      };

      await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData)
        .expect(403);
    });

    it('should require authentication', async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Unauthorized Document',
      };

      await request(app.server)
        .post('/api/documents')
        .send(documentData)
        .expect(401);
    });
  });

  describe('GET /api/documents', () => {
    let testDocumentId: string;

    beforeEach(async () => {
      // Create test document
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'registration',
        title: 'Test Document for Listing',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData);

      testDocumentId = response.body.id;
    });

    it('should list user documents', async () => {
      const response = await request(app.server)
        .get('/api/documents')
        .set('Authorization', authToken)
        .expect(200);

      expect(Array.isArray(response.body)).toBe(true);
      expect(response.body.length).toBeGreaterThan(0);
      expect(response.body.some((doc: any) => doc.id === testDocumentId)).toBe(true);
    });

    it('should filter documents by vehicle', async () => {
      const response = await request(app.server)
        .get('/api/documents')
        .query({ vehicleId: testVehicleId })
        .set('Authorization', authToken)
        .expect(200);

      expect(Array.isArray(response.body)).toBe(true);
      response.body.forEach((doc: any) => {
        expect(doc.vehicle_id).toBe(testVehicleId);
      });
    });

    it('should filter documents by type', async () => {
      const response = await request(app.server)
        .get('/api/documents')
        .query({ type: 'registration' })
        .set('Authorization', authToken)
        .expect(200);

      expect(Array.isArray(response.body)).toBe(true);
      response.body.forEach((doc: any) => {
        expect(doc.document_type).toBe('registration');
      });
    });
  });

  describe('GET /api/documents/:id', () => {
    let testDocumentId: string;

    beforeEach(async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Single Document Test',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData);

      testDocumentId = response.body.id;
    });

    it('should get single document', async () => {
      const response = await request(app.server)
        .get(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .expect(200);

      expect(response.body).toMatchObject({
        id: testDocumentId,
        user_id: testUserId,
        vehicle_id: testVehicleId,
        title: 'Single Document Test',
      });
    });

    it('should return 404 for non-existent document', async () => {
      await request(app.server)
        .get('/api/documents/non-existent-id')
        .set('Authorization', authToken)
        .expect(404);
    });
  });

  describe('PUT /api/documents/:id', () => {
    let testDocumentId: string;

    beforeEach(async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Document to Update',
        notes: 'Original notes',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData);

      testDocumentId = response.body.id;
    });

    it('should update document metadata', async () => {
      const updateData = {
        title: 'Updated Document Title',
        notes: 'Updated notes',
        details: { updated: true },
      };

      const response = await request(app.server)
        .put(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .send(updateData)
        .expect(200);

      expect(response.body).toMatchObject({
        id: testDocumentId,
        title: 'Updated Document Title',
        notes: 'Updated notes',
        details: { updated: true },
        updated_at: expect.any(String),
      });
    });

    it('should return 404 for non-existent document', async () => {
      await request(app.server)
        .put('/api/documents/non-existent-id')
        .set('Authorization', authToken)
        .send({ title: 'New Title' })
        .expect(404);
    });
  });

  describe('File Upload/Download Flow', () => {
    let testDocumentId: string;
    let testFilePath: string;

    beforeAll(() => {
      // Create test file
      testFilePath = path.join(__dirname, 'test-file.pdf');
      fs.writeFileSync(testFilePath, 'Fake PDF content for testing');
    });

    afterAll(() => {
      // Clean up test file
      if (fs.existsSync(testFilePath)) {
        fs.unlinkSync(testFilePath);
      }
    });

    beforeEach(async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Document for Upload Test',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData);

      testDocumentId = response.body.id;
    });

    it('should upload file to document', async () => {
      const response = await request(app.server)
        .post(`/api/documents/${testDocumentId}/upload`)
        .set('Authorization', authToken)
        .attach('file', testFilePath)
        .expect(200);

      expect(response.body).toMatchObject({
        id: testDocumentId,
        storage_bucket: expect.any(String),
        storage_key: expect.any(String),
        file_name: 'test-file.pdf',
        content_type: expect.any(String),
        file_size: expect.any(Number),
      });

      expect(response.body.storage_key).toMatch(/^documents\//);
    });

    it('should reject unsupported file types', async () => {
      // Create temporary executable file
      const execPath = path.join(__dirname, 'test.exe');
      fs.writeFileSync(execPath, 'fake executable');

      try {
        await request(app.server)
          .post(`/api/documents/${testDocumentId}/upload`)
          .set('Authorization', authToken)
          .attach('file', execPath)
          .expect(415);
      } finally {
        if (fs.existsSync(execPath)) {
          fs.unlinkSync(execPath);
        }
      }
    });

    it('should download uploaded file', async () => {
      // First upload a file
      await request(app.server)
        .post(`/api/documents/${testDocumentId}/upload`)
        .set('Authorization', authToken)
        .attach('file', testFilePath);

      // Then download it
      const response = await request(app.server)
        .get(`/api/documents/${testDocumentId}/download`)
        .set('Authorization', authToken)
        .expect(200);

      expect(response.headers['content-disposition']).toContain('test-file.pdf');
      expect(response.body.toString()).toBe('Fake PDF content for testing');
    });

    it('should return 404 for download without uploaded file', async () => {
      await request(app.server)
        .get(`/api/documents/${testDocumentId}/download`)
        .set('Authorization', authToken)
        .expect(404);
    });
  });

  describe('DELETE /api/documents/:id', () => {
    let testDocumentId: string;

    beforeEach(async () => {
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'registration',
        title: 'Document to Delete',
      };

      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', authToken)
        .send(documentData);

      testDocumentId = response.body.id;
    });

    it('should soft delete document', async () => {
      await request(app.server)
        .delete(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .expect(204);

      // Verify document is no longer accessible
      await request(app.server)
        .get(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .expect(404);
    });

    it('should return 404 for already deleted document', async () => {
      // Delete once
      await request(app.server)
        .delete(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .expect(204);

      // Try to delete again
      await request(app.server)
        .delete(`/api/documents/${testDocumentId}`)
        .set('Authorization', authToken)
        .expect(204); // Idempotent behavior
    });
  });

  describe('Authorization and Ownership', () => {
    let otherUserDocumentId: string;

    beforeEach(async () => {
      // Create document as different user
      const documentData = {
        vehicle_id: testVehicleId,
        document_type: 'insurance',
        title: 'Other User Document',
      };

      // Mock different user context
      const otherUserToken = 'Bearer other-user-token';
      const response = await request(app.server)
        .post('/api/documents')
        .set('Authorization', otherUserToken)
        .send(documentData);

      otherUserDocumentId = response.body.id;
    });

    it('should not allow access to other users documents', async () => {
      await request(app.server)
        .get(`/api/documents/${otherUserDocumentId}`)
        .set('Authorization', authToken)
        .expect(404);
    });

    it('should not allow update of other users documents', async () => {
      await request(app.server)
        .put(`/api/documents/${otherUserDocumentId}`)
        .set('Authorization', authToken)
        .send({ title: 'Hacked Title' })
        .expect(404);
    });
  });
});