/**
 * Integration tests for the scaffold endpoint.
 * Tests GET /sdk/scaffold/:agentId
 */

import request from 'supertest';
import express from 'express';
import { Pool } from 'pg';
import { AuditService } from '../../src/services/AuditService';
import { ScaffoldService } from '../../src/services/ScaffoldService';
import { ScaffoldController } from '../../src/controllers/ScaffoldController';
import { createScaffoldRouter } from '../../src/routes/scaffold';

jest.mock('../../src/services/AuditService');
const MockAuditService = AuditService as jest.MockedClass<typeof AuditService>;

const mockQuery = jest.fn();
const mockPool = { query: mockQuery } as unknown as Pool;

const AGENT_ID = 'agent-uuid-integration-test';
const TENANT_ID = 'org_system';
const CLIENT_ID = 'credential-client-id';

// Auth middleware injecting a test user that owns the agent
const testAuth = (req: express.Request, _res: express.Response, next: express.NextFunction) => {
  req.user = {
    sub: AGENT_ID,
    client_id: CLIENT_ID,
    scope: 'agents:read',
    jti: 'jti-test',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600,
    organization_id: TENANT_ID,
  };
  next();
};

// Auth middleware for a different tenant (forbidden)
const otherTenantAuth = (req: express.Request, _res: express.Response, next: express.NextFunction) => {
  req.user = {
    sub: 'other-agent-uuid',
    client_id: 'other-client',
    scope: 'agents:read',
    jti: 'jti-other',
    iat: Math.floor(Date.now() / 1000),
    exp: Math.floor(Date.now() / 1000) + 3600,
    organization_id: 'other-org',
  };
  next();
};

function buildApp(authMiddleware = testAuth) {
  const app = express();
  app.use(express.json());

  const auditService = new MockAuditService({} as never) as jest.Mocked<AuditService>;
  auditService.logEvent = jest.fn().mockResolvedValue({});

  const scaffoldService = new ScaffoldService();
  const controller = new ScaffoldController(scaffoldService, mockPool, auditService);
  app.use('/api/v1', createScaffoldRouter(controller, authMiddleware));

  app.use(
    (
      err: { httpStatus?: number; code?: string; message?: string },
      _req: express.Request,
      res: express.Response,
      _next: express.NextFunction,
    ) => {
      res.status(err.httpStatus ?? 500).json({ code: err.code ?? 'ERROR', message: err.message });
    },
  );

  return app;
}

describe('Scaffold Endpoint', () => {
  beforeEach(() => {
    jest.clearAllMocks();
    process.env['API_URL'] = 'https://api.sentryagent.ai';
  });

  afterEach(() => {
    delete process.env['API_URL'];
  });

  describe('GET /api/v1/sdk/scaffold/:agentId', () => {
    it('returns a TypeScript scaffold ZIP with correct Content-Type and Content-Disposition', async () => {
      // Agent exists and belongs to tenant
      mockQuery
        .mockResolvedValueOnce({
          rows: [{ agent_id: AGENT_ID, email: 'my-agent@test.com', organization_id: TENANT_ID }],
        })
        // Credential lookup
        .mockResolvedValueOnce({ rows: [{ client_id: CLIENT_ID }] });

      const app = buildApp();
      const res = await request(app).get(`/api/v1/sdk/scaffold/${AGENT_ID}?language=typescript`);

      expect(res.status).toBe(200);
      expect(res.headers['content-type']).toBe('application/zip');
      expect(res.headers['content-disposition']).toMatch(/attachment; filename=".*typescript\.zip"/);
      // Response body should be a non-empty buffer (ZIP magic bytes PK)
      expect(res.body).toBeDefined();
    });

    it('returns a Python scaffold ZIP', async () => {
      mockQuery
        .mockResolvedValueOnce({
          rows: [{ agent_id: AGENT_ID, email: 'my-agent@test.com', organization_id: TENANT_ID }],
        })
        .mockResolvedValueOnce({ rows: [{ client_id: CLIENT_ID }] });

      const app = buildApp();
      const res = await request(app).get(`/api/v1/sdk/scaffold/${AGENT_ID}?language=python`);

      expect(res.status).toBe(200);
      expect(res.headers['content-type']).toBe('application/zip');
    });

    it('returns HTTP 400 for an invalid language', async () => {
      const app = buildApp();
      const res = await request(app).get(`/api/v1/sdk/scaffold/${AGENT_ID}?language=cobol`);

      expect(res.status).toBe(400);
    });

    it('returns HTTP 404 when agent does not exist', async () => {
      mockQuery.mockResolvedValueOnce({ rows: [] });

      const app = buildApp();
      const res = await request(app).get(`/api/v1/sdk/scaffold/${AGENT_ID}?language=typescript`);

      expect(res.status).toBe(404);
    });

    it('returns HTTP 403 when agent belongs to a different tenant', async () => {
      mockQuery.mockResolvedValueOnce({
        rows: [{ agent_id: AGENT_ID, email: 'my-agent@test.com', organization_id: TENANT_ID }],
      });

      const app = buildApp(otherTenantAuth);
      const res = await request(app).get(`/api/v1/sdk/scaffold/${AGENT_ID}?language=typescript`);

      expect(res.status).toBe(403);
    });
  });
});
